数据结构与算法(一)插入排序
1. 插入排序
1.1 排序问题
输入:n个数的一个序列 A = < a 1 a_1 a1 , a 2 a_2 a2, …, a n a_n an>。
输出:输入序列的一个排序 A ′ A^\prime A′ = < a 1 ′ a_1^\prime a1′ , a 2 ′ a_2\prime a2′, …, a n ′ a_n\prime an′ >,满足 a 1 ′ a_1^\prime a1′ ≤ \leq ≤ a 2 ′ a_2\prime a2′ ≤ \leq ≤ … ≤ \leq ≤ a n ′ a_n\prime an′ 。
原理:
从整个待排序列中选出一个元素插入到已经有序的子序列中去,得到一个有序的、元素加一的子序列,直到整个序列的待插入元素为0,则整个序列全部有序。
在实际的算法中,我们经常选择序列的第一个元素作为有序序列(因为一个元素肯定是有序的),我们逐渐将后面的元素插入到前面的有序序列中,直到整个序列有序。
1.2 实现伪代码
INSERTION-SORT(A)
for 2 to A.length
key = A[j];
// Insert A[j] into the sorted sequence A[1..j-1]
i = j - 1;
while i > 0 and A[j] > key
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
1.3 步骤解析
举例:对数组A = <5, 2, 4, 6,1, 3>进行插入排序(升序)
1.3 算法分析
假设第i行的每次执行时间为 a i a_i ai。
INSERTION-SORT(A) | 代价 | 次数 |
---|---|---|
for 2 to A.length | c 1 c_1 c1 | n |
key = A[j]; | c 2 c_2 c2 | n - 1 |
// Insert A[j] into the sorted sequence A[1…j-1] | 0 | n - 1 |
i = j - 1; | c 4 c_4 c4 | n - 1 |
while i > 0 and A[j] > key | c 5 c_5 c5 | ∑ j = 2 n \sum_{j=2}^n ∑j=2n t j t_j tj |
A[i + 1] = A[i] | c 6 c_6 c6 | ∑ j = 2 n \sum_{j=2}^n ∑j=2n( t j t_j tj - 1) |
i = i - 1 | c 7 c_7 c7 | ∑ j = 2 n \sum_{j=2}^n ∑j=2n( t j t_j tj - 1) |
A[i + 1] = key | c 8 c_8 c8 | n - 1 |
其中:
- t j t_j tj表示对那个值j第5行执行while循环的次数
- 当一个for 或 while循环按通常的方式(即由于循环头中的测试)退出时,执行测试的次数比执行循环体的次数多1
- 注释是不可执行的语句,所以不需要时间
由上表的次数和代价相乘我们可以得到运行时间T[n]:
T(n) = c 1 c_1 c1n + c 2 c_2 c2(n - 1) + c 4 c_4 c4(n - 1) + c 5 c_5 c5 ∑ j = 2 n \sum_{j=2}^n ∑j=2n t j t_j tj + c 6 c_6 c6 ∑ j = 2 n \sum_{j=2}^n ∑j=2n( t j t_j tj - 1) + c 7 c_7 c7 ∑ j = 2 n \sum_{j=2}^n ∑j=2n( t j t_j tj - 1) + c 8 c_8 c8(n - 1)
1.3.1 最佳情况
我们都知道,当输入数组已经排好序时是插入排序的最佳情况。即在伪代码的第5行,i 取初值 j - 1时, 有A[j]
≤
\leq
≤ key。从而对于j = 2, 3, … ,n,有
t
j
t_j
tj = 1。故最佳情况的运行时间为:
T ( n ) = c 1 T(n) = c_1 T(n)=c1n + c 2 c_2 c2(n - 1) + c 4 c_4 c4(n - 1) + c 5 c_5 c5(n - 1) + c 8 c_8 c8(n - 1)
= ( c 1 + c 2 + c 4 + c 5 + c 8 ) n − ( c 2 + c 4 + c 5 + c 8 ) = (c_1+ c_2 + c_4 + c_5 + c_8)n - (c_2 + c_4 + c_5 + c_8) =(c1+c2+c4+c5+c8)n−(c2+c4+c5+c8)
= Θ ( n ) = \Theta(n) =Θ(n)
1.3.2 最坏情况
而最坏情况则是,每个元素A[j]都要和整个已排序子数组A[1…j - 1] 中的每个元素进行比较,所以 j = 2, 3, …, n,有 t j = j t_j = j tj=j .
则有: ∑ j = 2 n j = n ( n + 1 ) 2 − 1 \sum_{j=2}^n j = \frac{n(n+1)}{2}-1 ∑j=2nj=2n(n+1)−1 和 ∑ j = 2 n ( j − 1 ) = n ( n − 1 ) 2 \sum_{j=2}^{n}(j-1) = \frac{n(n-1)}{2} ∑j=2n(j−1)=2n(n−1)
T(n) = c_1 n + c 2 c_2 c2(n-1) + c 4 c_4 c4(n-1) + c 5 c_5 c5( n ( n + 1 ) 2 − 1 \frac{n(n+1)}{2}-1 2n(n+1)−1) + c 6 ( n ( n − 1 ) 2 ) c_6(\frac{n(n-1)}{2}) c6(2n(n−1)) + c 7 c_7 c7( n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1)) + c 8 c_8 c8(n-1)
= ( c 5 2 + c 6 2 + c 7 2 ) (\frac{c_5}{2} + \frac{c_6}{2} +\frac{c_7}{2} ) (2c5+2c6+2c7) n 2 n^2 n2 + ( c 1 + c 2 + c 4 + c 5 2 − c 6 2 c 7 2 + c 8 ) (c_1 + c_2 +c_4 + \frac{c_5}{2} -\frac{c_6}{2} \frac{c_7}{2} + c_8) (c1+c2+c4+2c5−2c62c7+c8) n − n- n−( c 2 + c 4 + c 5 + c 8 c_2+c_4+c_5+c_8 c2+c4+c5+c8)
= Θ ( n 2 ) \Theta(n^2) Θ(n2)
1.3.3两种情况的区别
相比于最佳情况,对于我们分析算法优劣最有用的应该是最坏情况,因为:
- 算法的最坏情况提供了一个上界,知道了上界能确保算法不需要更长的时间。
- 对于某些算法最坏情况经常出现。
- “平均情况”往往与最坏情况大致一样差。