欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

快速排序的时间复杂性和预期比较次数

最编程 2024-03-24 09:04:50
...

众所周知,快速排序的时间复杂度为\(O(n\text{lg}n)\)。虽然对此很容易直观理解,但由于算法的随机特性,这一时间复杂度的严格证明并非显然的。我将在这里说明如何计算快速排序运行过程中的比较次数的期望,以此得到对时间复杂度的较为严谨的证明。


定义\(E(n)\)为对长度为\(n\)的数组进行排序所需要的期望比较次数。考虑算法的划分过程,由于基准元素选择的随机性,每一种划分结果的可能性是相等的。对每种情况下的比较次数取平均值,即可得到下面的递推式:$$E(n)=\frac{1}{n-1}\sum_{k=1}^{n-1} \left [E(k)+E(n-k)+n \right ]$$变量\(k\)枚举了不同的划分情况,\(n\)表示划分过程中的比较次数。\(E(0)=E(1)=0\),因为空数组或只有一个元素的数组不需要比较。

得出递推式后,剩下的就是数学推导了qwq
首先,我们注意到方括号里的\(n\)可以从求和符号里提取出来,而另外两项是对称的。于是等式可以改写成下面的形式:

\[E(n)=n+\frac{2}{n-1}\sum_{k=1}^{n-1} E(k) \]

等式中包含太多项。注意右侧的前缀求和形式,我们可以定义前缀和\(s(n)=\sum_{k=1}^{n} E(k)\)。等式再度简化:

\[s(n)-s(n-1)=n+\frac{2}{n-1}s(n-1) \]

整理得到:

\[s(n)=n+\frac{n+1}{n-1}s(n-1) \]

等号两边同时除以\(n(n+1)\),得到

\[\frac{s(n)}{n(n+1)}=\frac{1}{n+1}+\frac{s(n-1)}{(n-1)n} \]

再做个换元,令

\[t(n)=\frac{s(n)}{n(n+1)} \]

局面顿时明了起来:

\[t(n)=t(n-1)+\frac{1}{n+1} \]

反复运用这一递推式得到

\[t(n)=\frac{1}{3}+\frac{1}{4}+\frac{1}{5}+\cdots+\frac{1}{n}+\frac{1}{n+1} \]

这就是我们很熟悉的东西啦。让我们把调和级数的前\(n\)项和记作\(H_n\)\(t(n)\)就可以写成这样的形式:

\[t(n)=H_{n+1}-\frac{3}{2} \]

接下来事情就顺理成章了:

\[\begin{align} &s(n)=n(n+1)t(n)=n(n+1)\left(H_{n+1}-\frac{3}{2}\right) \notag \\&E(n)=s(n)-s(n-1)\notag \\&\;\;\;\;\;\;\;\;\:=n[(n+1)H_{n+1}-(n-1)H_n-3]\notag \\&\;\;\;\;\;\;\;\;\:=n(\frac{n}{n+1}+H_{n+1}+H_n-3)\notag \\&\;\;\;\;\;\;\;\;\:=n\left[\frac{n}{n+1}+(H_{n}+\frac{1}{n+1})+H_n-3\right]\notag \\&\;\;\;\;\;\;\;\;\:=2n(H_n-1)\notag \\&\;\;\;\;\;\;\;\;\:=2n\left[\int_{1}^{n}\frac{1}{x}dx+O(1)\right]\notag \\&\;\;\;\;\;\;\;\;\:=2n\left[\text{ln}n+O(1)\right]\notag \\&\;\;\;\;\;\;\;\;\:=O(n\text{lg}n)\notag \end{align}\]


\(Q.E.D.\;\;\;\;\;(\text{Quite Easily Done})\)

推荐阅读