结论:插入排序的时间复杂度的更精确的量度应该为: Θ ( n + m ) \Theta(n+m) Θ(n+m),其中n为数组规模,m为数组逆序对数。
众所周知,插入排序的最佳时间复杂度为 Θ ( n ) \Theta(n) Θ(n),平均时间复杂度和最差时间复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2),那么给出任意一个输入序列,如何计算使用插排的运行时间?
首先考虑,随着数组的无序程度增加,运行时间自然会增加,而这个无序程度的最直白的量度就应当是逆序对数。
给出插入排序的伪代码:
1 for i = 2 to n: // 从1开始计数
2 key = A[i]
3 // Insert A[i] into the sorted subarray A[1:i–1].
4 j = i – 1
5 while j > 0 and A[j] > key
6 A[j + 1] = A[j]
7 j = j – 1
8 A[j + 1] = key
通过分析每一行代码的执行次数,我们就可以粗略得出排序的运行时间。
-
首先,1,2,4,8行,各执行n次,则总执行次数为n次。(不考虑常数项、常数因子和注释行)。
-
其次,设在第
i
次迭代中,内循环的5,6,7行各执行 t i t_i ti次,那么总执行次数为 ∑ i = 2 n t i \sum_{i=2}^n t_i ∑i=2nti次。设A的逆序对数为m
。
现在仅剩
t
i
t_i
ti没有清楚的数量定义,考虑插入排序的性质:当插入A[i]
时,A[1...i-1]
均已按照升序拍好,而现在要将A[i]插入到一个位置,使得该位置前面的元素均不大于A[i],后面的元素均大于A[i]。容易分析得到,
t
i
t_i
ti=A[1...i-1]中比A[i]大的元素数量
,也就是以A[i]为后件的逆序对数。因此
∑
i
=
2
n
t
i
=
m
\sum_{i=2}^n t_i=m
∑i=2nti=m。
因此,对插入排序的时间复杂度的更精确的量度应该为: Θ ( n + m ) \Theta(n+m) Θ(n+m),其中n为数组规模,m为数组逆序对数。而最佳复杂度和最差复杂度即为该量度的一个特例(顺序m=0;逆序m= n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1))。