首页 > 代码库 > 【算法导论】插入排序

【算法导论】插入排序

排序问题

   输入: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. 初始化:循环的第一次迭代之前,它为真。
  2. 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真。
  3. 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。

插入排序循环不变式

   在第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 }

 

【算法导论】插入排序