排序算法之插入排序
1)基本思想:
对于大小为N的数组A,插入排序的基本思想是将位置为P的元素插入前面已排好序的从0到P-1的正确位置中,这样一来,从0到P的元素都是排好序的,接着对后面的元素做插入排序,所以总共需要N-1趟插入排序。下面的示例中初始数组为34 8 64 51 32 21,分别对P=1, 2...5位置的元素做插入排序,每一次排序结束后从0到P位置的元素都处于已排好序状态。
初始数组 | 34 8 64 51 32 21 | 描述 |
P = 1 | 8 34 64 51 32 21 | 8需要移动 |
P = 2 | 8 34 64 51 32 21 | 64不需要移动 |
P = 3 | 8 34 51 64 32 21 | 51需要移动 |
P = 4 | 8 32 34 51 64 21 | 32需要移动 |
P = 5 | 6 21 32 34 51 64 | 21需要移动 |
2)算法描述:
算法的思想是对P位置的元素排序时,前面的0到P-1位置的元素已排好序,此时从位置P-1的元素开始与A[P]比较,如果A[P-1]>A[P],那么将元素A[P-1]向后移动一个位置,接着比较A[P-2]与A[P],直到找到元素A[P]的正确位置,将元素放入该位置,即完成一次插入排序。从元素A[1]到A[N-1]共需要N-1次插入排序。代码如下:
1 void InsertionSort(ElementType A[], int n) 2 { 3 ElementType temp; 4 for(int P = 1; P < N; P++) 5 { 6 int k = P; 7 temp = A[P]; 8 for(; k > 0 && A[k-1] > temp; k--) 9 { 10 A[k] = A[k-1]; 11 } 12 A[k] = temp; 13 } 14 }
3)复杂度分析:
最坏情况下,初始数组是反序,如64 51 34 32 21 6,那么对位置为P的元素进行一次插入排序需要做P+1次赋值操作,由于需要N-1次插入,那么时间代价为:
T = 2 + 3 + ... + N-1 = O(N^2)
最好情况下,初始数组是已排好序的,那么for循环的测试条件会在最开始的时候就不成立,也就不会执行赋值语句,这样的情况下,只需要N-1次循环操作,循环内部只需要一次测试动作,所以时间复杂度为O(N)。
平均情况下的时间复杂度下界为,证明中引入了逆序的概念:
如果数组A中,对于i < j, A[i] > A[j],那么(A[i],A[j])就构成了一个逆序,因此对于初始数组34 8 64 51 32 21,逆序有:
(34,8),(34,32),(34,21),(64,51),(64,32),(64,21),(51,32),(51,21),(32,21)。
逆序的个数和交换的次数是一致的,这是很好理解的,比如(34,21)是一个逆序,那么这两个元素之间早晚都会有一次比较并导致一次移动。对于一个随机的互异的数组,任意两个数的无序组合为N(N-1)/2,所有的组合要么在原数组中是逆序,要么是在原数组的反序中是逆序,所以逆序的平均数目为N(N-1)/4,那么对应的插入排序平均移动次数为N(N-1)/4 = 。
以上整理于数据结构与算法分析(C语言描述)第二版 第七章 插入排序