本章的重点是循环不变式。也就是在一个循环中存在着某些不变的量。它相似于数学概括法的概括步骤: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; }