首页 > 代码库 > 【算法导论】插入排序
【算法导论】插入排序
排序问题
输入:n个数的一个序列<a1, a2, ..., an>
输出:输入序列的一个排列<b1, b2, ..., bn>,满足 b1 ≤ b2 ≤ ... ≤ bn。
插入排序
对于插入排序,我们将其伪代码命名为Insertion-sort,其中的参数是一个数组A[1..n],包含长度为n的要排序的一个序列。(在代码中,A中元素的数目n用A.length来表示。)该算法原址排序输入的数:算法在数组A中重排这些数,在任何时候,最多只有其中的常数个数字存储在数组外面。在过程Insertion-sort结束时,输入数组A包含排序好的输出序列。
Insertion-sort(A) 1 for (j = 2; j <= A.length; j++) 2 key = A[j] 3 // Insert A[j] into the sorted sequence A[1..j-1] 4 i = j - 1 5 while (i > 0 && A[i] > key) 6 A[i+1] = A[i] 7 i = i - 1 8 A[i+1] = key
循环不变式
循环不变式主根用来帮助我们理解算法的正确性。关于循环不变式,我们必须证明三条性质:
- 初始化:循环的第一次迭代之前,它为真。
- 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真。
- 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。
插入排序循环不变式
在第1-8行的for循环的每次迭代开始时,子数组A[1..j-1]由原来在A[1..j-1]中的元素组成,但已按序排列。
【初始化】
首先证明在第一次循环迭代之前(当j=2时),循环不变式成立。
所以子数组A[1..j-1]仅由单个元素A[1]组成,实际上就是A[1]中原来的元素。而且该子数组是排序好的(当然很平凡)。这表明第一次循环迭代之前循环不变式成立。
【保持】
其次是第二条性质:证明每次迭代保持循环不变式。
非形式化的,for循环体的第4-7行将A[j-1]、A[j-2]、A[j-3]等向右移动一个位置,直到找到A[j]的适当位置,第8行将A[j]的值插入该位置。这时子数组A[1..j]由原来在A[1..j]中的元素组成,但已按序排列。那么对for循环的下一次迭代增加j将保持循环不变式。
【终止】
最后研究在循环终止时发生了什么。导致for循环终止的条件是 j > A.length = n。因为每次循环迭代j增加1,那么必有j=n+1。在循环不变式的表述中将j用n+1代替,我们有:子数组A[1..n]由原来在A[1..n]中的元素组成,但已按序排列。注意到,子数组A[1..n]就是整个数组,我们推断出整个数组已排序。因此算法正确。
时间复杂度分析
最好情况:输入数组已经有序。此时时间复杂度为O(n)。
最坏情况:输入数组为逆序排列。此时时间复杂度为O(n2)。
Exercise 2.1
1.说明Insertion-sort在数组A=<31, 41, 59, 26, 41, 58>上的执行过程。
2.重写Insertion-sort,使之按非升序(而不是非降序)排序。
3.考虑以下查找问题:
输入:n个数的一个序列A=<a1, a2, ..., an>和一个值v。
输出:下标i使得v=A[i]或者当v不在A中出现时,v为特殊值NIL。
写出线性查找算法的伪代码,它扫描整个序列来查找v。使用一个循环不变式来证明你的算法是正确的。确保你的循环不变式满足三条必要的性质。
4.考虑把两个n位二进制整数加起来的问题,这两个整数分别存储在两个n元数组A和B中。这两个整数的和应按二进制形式存储在一个(n+1)元数组C中。请给出该问题的形式化描述,并写出伪代码。
附录:插入排序代码示例
1 /** 2 * 如果这段代码好用,那么它是xiaoxxmu写的 3 * 否则,我也不知道是谁写的 4 */ 5 #include <stdio.h> 6 7 void insertion_sort(int arr[], int length) 8 { 9 for (int i = 1; i < length; i++) 10 { 11 int key = arr[i], j = i - 1; 12 while (j >= 0 && arr[j] > key) 13 { 14 arr[j+1] = arr[j]; 15 j--; 16 } 17 arr[j+1] = key; 18 } 19 } 20 21 int main(int argc, char *argv[]) 22 { 23 int arr[] = {5, 7, 3, 9, 4, 2, 6, 1, 8}; 24 int length = (sizeof(arr)) / (sizeof(arr[0])); 25 insertion_sort(arr, length); 26 for (int i = 0; i < length; i++) 27 { 28 if (i == 0) printf("%d", arr[i]); 29 else printf(" %d", arr[i]); 30 } 31 printf("\n"); 32 return 0; 33 }
【算法导论】插入排序