本章的重點是循環不變式。也就是在一個循環中存在着某些不變的量。它相似於數學概括法的概括步驟:node
咱們在設計一個算法、分析一個算法的時候,要適當應用循環不變式來簡化分析工做、證實算法的正確性。算法
書中舉例插入排序:數組
n個元素的待排序數組A,下標是從1到n。 j從2開始一直遍歷到大於n(此時會退出循環)。循環不變式的量是:A[1...j-1]這個子數組是已排序的。 spa
經過這個簡單的約束規則,能夠很容易地默寫出插入排序的實現代碼:設計
void insert_sort(int* A, int n) { for (int j = 1; j < n;j++) { int key = A[j]; int i = j-1; for (; i>=0 && key < A[i]; i--) { A[i+1] = A[i]; } A[i+1] = key; } }
存在着兩層for循環,因此算法的時間複雜度是O(n^2).
只用了一個臨時變量key, 因此空間複雜度是O(1)code
擴展思考一下:插入排序能夠對鏈表進行排序嗎?blog
應該是能夠的。假設存在鏈表list. 每次從list中摘取一個節點,將它插入到sortedList中,一直到list爲空。循環不變式sortedList是有序的鏈表。排序
因而循環不變式是:數學
先定義一下listit
typedef struct List{ int item; struct List* next; } List; List* AddList(List* l, int item);
#include "list.h" List* AddList(List* l, int item) { List* node = new(List);// (List*) malloc(sizeof(List)); node->item = item; node->next = 0; if (l) { node->next = l->next; l->next = node; return l; } return node; }
再來實現一下鏈表的插入排序
List* insert_sortL(List* list) { List* head = list; if (head == NULL) { return NULL; } List* sortedList = head;//初始化,sortedList只有一個節點。 List* j = head->next; sortedList->next = NULL;//將head從list中摘取出來 for (; j != NULL;) { List* current = j;//注意摘取節點j(current)的次序 j = j->next; current->next = NULL; int k = current->item; if (k < sortedList->item) {//若是比sortedList的頭結點小,則須要更新頭結點 current->next = sortedList; sortedList = j; continue; } List* i = sortedList;//將current插入到sortedList的合適位置 for (; i->next != NULL && k > i->next->item; i = i->next) { } current->next = i->next; i->next = current; } return sortedList; }