插入排序的运行时间在输入数据已经排序的情况下为Ω(n),在输入数据为逆序的情况下为O(n2),平均情况下它的运行时间为Θ(n2)。
为什么会这样呢?例如,为什么平均情况不更接近于O(n log n)呢?
插入排序的运行时间在输入数据已经排序的情况下为Ω(n),在输入数据为逆序的情况下为O(n2),平均情况下它的运行时间为Θ(n2)。
为什么会这样呢?例如,为什么平均情况不更接近于O(n log n)呢?
为了回答这个问题,让我们首先确定如何评估插入排序的运行时间。如果我们可以找到一个好的数学表达式来表示运行时间,那么我们就可以操作这个表达式来确定平均运行时间。
我们需要关注的关键观察是,插入排序的运行时间与输入数组中的逆序对数量密切相关。在数组中,逆序对是一对元素A[i]和A[j],它们的相对顺序是错误的 - 也就是说,i < j,但A[j] < A[i]。例如,在这个数组中:
0 1 3 2 4 5
有一个反转:3和2应该交换。在这个数组中:
4 1 0 3 2
一共有6个逆序对:
逆序对的一个重要性质是,排序后的数组中没有逆序对,因为每个元素都应该比它后面的所有元素小,比它前面的所有元素大。
这个性质很重要的原因是,插入排序中所做的工作量与原始数组中的逆序对数量之间存在直接联系。为了看清这一点,让我们回顾一下插入排序的一些快速伪代码:
[---- X ----] A[j] A[j+1] [---- Y ----]
在这里,X是交换对之前数组的一部分,Y是交换对之后数组的一部分。
假设我们交换A[j]和A[j+1]。那么逆序对的数量会发生什么变化呢?好吧,让我们考虑两个元素之间的任意逆序对。有6种可能性:
我们对E[I]感兴趣,它是数组中反转的期望数量。使用期望的线性性,这可以表示为:I = Σ Xij
E[I] = ΣE[Xij] = Σ (1 / 2)
由于总共有n(n - 1)/2个项,因此结果为
E[I] = n(n - 1) / 4 = Θ(n2)
因此,期望逆序对的数量是Θ(n2),期望运行时间是Θ(n2 + n) = Θ(n2)。这就解释了为什么插入排序的平均情况下的时间复杂度是Θ(n2)。
希望这可以帮到你!
为了好玩,我写了一个程序,它可以遍历大小为n的向量的所有数据组合,计算比较次数,并发现最好的情况是n-1(完全排序),最坏的情况是(n*(n-1))/2。
以下是不同n的一些结果:
n min ave max ave/(min+max) ave/max
2 1 1 1 0.5000
3 2 2.667 3 0.5334
4 3 4.917 6 0.5463
5 4 7.717 10 0.5512
6 5 11.050 15 0.5525
7 6 14.907 21 0.5521
8 7 19.282 28 0.5509
9 8 24.171 36 0.5493
10 9 29.571 45 0.5476
11 10 35.480 55 0.5458
12 11 41.897 66 0.5441
看起来平均值更接近最小值而不是最大值。
编辑:一些额外的数值
13 12 48.820 78 0.5424
14 13 56.248 91 0.5408
编辑:15的值
15 14 64.182 105 0.5393
编辑:选择更高的值
16 15 72.619 120 - 0.6052
32 31 275.942 496 - 0.5563
64 63 1034.772 1953 - 0.5294
128 127 4186.567 8128 - 0.5151
256 255 16569.876 32640 - 0.5077
最近我编写了一个程序,用于计算插入排序在较高n值情况下的平均比较次数。通过这些数据,我得出结论:当n趋近于无穷大时,平均情况会趋近于最坏情况除以二。
Loop : j = 1 to n
{
temp = array[j]
k = j - 1
Loop until : ( k > 0 ) and ( array[k] > temp ) {
array[k+1] = array[k] // shifting one element at a time
k = k - 1
}
array[k+1] = temp
}
外层循环:1 - 2 - 3 - 4 - 5 - .... n
内层循环:对于每个元素都有自己的内层循环
让我们以数组为例:[ 3, 2, 9, 1, 2, 6, 5 ](平均情况)
3 - 2 - 9 - 1 - ..... n
| | | |
no. of loop 1 0 3 (n + 1) / 2
(n+1)/2 -> (multiple cases. so, using median of all probabilities)
因此,对于每个元素 (n),循环运行 ( (n+1)/2 ) 次。
-> n(n+1)/2
-> (n2 + n )/2
-> n2 + n // drop constants
-> n2 // drop lower order terms
因此,即使对于平均情况,时间复杂度也为:O(n*n)
注意:所有与输入规模成比例增长的循环都具有线性时间复杂度O(n)。如果您只循环遍历数组的一半,那么这仍然是O(n)。请记住我们会删除常数,所以1/2 n => O(n)。