我同意你的假设,即平均时间复杂度仍然是O(n log n)
. 我不是专家,100% 肯定,但这些是我的想法:
这是就地快速排序的伪代码:(使用 l=1 和 r=数组长度调用快速排序)
Quicksort(l,r)
--------------
IF r-l>=1 THEN
choose pivot element x of {x_l,x_l+1,...,x_r-1,x_r}
order the array-segment x_l,...x_r in such a way that
all elements < x are on the left side of x // line 6
all elements > x are on the right side of x // line 7
let m be the position of x in the 'sorted' array (as said in the two lines above)
Quicksort(l,m-1);
Quicksort(m+1,r)
FI
然后,平均时间复杂度分析通过选择第 6 行和第 7 行中的“<”-比较作为该算法中的主要操作进行推理,最终得出平均时间复杂度为 O(n log n) 的结论。由于没有考虑“以这样的方式对数组段 x_l、...x_r 进行排序”的成本(如果你想找到界限,只有主导操作在时间复杂度分析中很重要),我认为“因为它在对它进行分区时必须执行两次列表传递”不是问题,因为您的 Haskell 版本在此步骤中只需要大约两倍的时间。附录操作也是如此,我同意这不会增加渐近成本:
因为追加和分区仍然具有线性时间复杂度,即使它们效率低下。
为了方便起见,我们假设这将“n”加到我们的时间复杂度成本中,因此我们有“O(n log n+n)”。由于存在一个自然数 o,对于所有大于 o 的自然数,n log n > n 都成立,因此您可以估计 n log n +n 到顶部 2 n log n 和底部 n log n,因此n log n+n = O(n log n)。
此外,选择第一个元素作为枢轴不是最佳选择。
我认为枢轴元素的选择在这里无关紧要,因为在平均案例分析中,您假设数组中元素的均匀分布。您不知道应该从数组中的哪个位置选择它,因此您必须考虑所有这些情况,在这些情况下,您的枢轴元素(独立于您取它的列表的哪个位置)是第 i-st 最小元素在您的列表中,对于 i=1...r。