归并排序,快速排序为什么快

对于一个 n n n个元素的数组,必须要确定两两之间的相对顺序,假设每次都抓取不同的二元组,需要 log ⁡ 2 n ! \log_2n! log2n!次比较,由于 log ⁡ 2 n ! ≈ n log ⁡ 2 n \log_2n!\approx n\log_2n log2n!nlog2n O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)就是排序算法的下界。

前面几周写过一篇散文:
https://zhuanlan.zhihu.com/p/429710417
但紧接着问题就来了,到底为什么归并排序,快速排序快呢?

这里有必要澄清一下两个问题的区别:

  • 归并排序,快速排序节省了哪些比较操作?
  • 归并排序,快速排序为什么能节省这些操作?

上面那篇文章里,似乎回答了第一个问题,但仔细一想,发现还差点(我已经做了批注)。

虽然可以看出具体省略了哪些比较操作,但背后的原因是什么?或者换一个问题,冒泡排序到底为什么慢?

冒泡排序实际上将数组中任意两个数字都进行了一次比较,问题在于,这些比较中,有一些是没有必要的。比如:

  • 比较1: a 2 < a 8 a_2<a_8 a2<a8
  • 比较2: a 8 < a 19 a_8<a_{19} a8<a19
  • 比较3: a 2 < a 19 a_2<a_{19} a2<a19

比较3还有意义吗?毫无意义!然而冒泡排序无法识别,它只是逐数字顺序比较。

对于 log ⁡ 2 n ! \log_2n! log2n!的分析,比较3是隐含在比较1和比较2两个比较中的,事实上当完成比较1和比较2后,在上面的比较结果下,比较3就没有必要了,除非比较1得出 a 2 > a 8 a_2>a_8 a2>a8或者比较2得出 a 8 > a 19 a_8>a_{19} a8>a19的结论,才需要比较3。

按照排列组合分析,可天然剔除掉可传递的关联比较, log ⁡ 2 n ! ≈ n log ⁡ 2 n \log_2n!\approx n\log_2n log2n!nlog2n次比较是不包括可传递的关联比较的。

接下来只要说明归并排序和快速排序的每次比较都是无关联即可,这个是显然的:

  • 归并排序:实际合并之前,两个子数组从没遇见过,不可能有任何关联。
  • 快速排序:左右两个子数组均按原始相对顺序和锚点比较,内部彼此无关联。

无论如何,归并排序均可达 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)的时间复杂度,对于快速排序,在最理想情况下,也可达到,即便是平均情况,离最理想情况也不远。

冒泡排序的算法本身就如此定义,随排序进行,未排序序列趋于有序,但排序过程无法识别哪些比较是不必要的,从信息论的角度,大概率的非必要比较依然要进行,这些比较带来的信息量无疑很小,这就是低效的根源,反过来,归并排序和快速排序依偎着比较排序的下界,这就是它们快的根源。

log ⁡ n ! ≈ n log ⁡ n \log n!\approx n\log n logn!nlogn事实上就是排序的最大熵,自然就是排序操作的下界。在没有预假设的情况下, n n n个数组的序列一共有 n ! n! n!种,升序(或者降序)只是其中一种,它的概率为 1 n ! \dfrac{1}{n!} n!1,排序信息熵为:

H = − ∑ i − 1 n ! 1 n ! log ⁡ 2 1 n ! = log ⁡ 2 n ! ≈ n log ⁡ n H=-\sum\limits_{i-1}^{n!}\dfrac{1}{n!}\log_2\dfrac{1}{n!}=\log_2n!\approx n\log n H=i1n!n!1log2n!1=log2n!nlogn

时间复杂度就是信息熵的度量,引入新的预设,信息熵必然减小,冒泡排序随着排序的进行,将引入新的预设,而归并排序和快速排序在排序过程中一直保持最开始的预设,保持比较操作的无关联性。

浙江温州皮鞋湿,下雨进水不会胖。

おすすめ

転載: blog.csdn.net/dog250/article/details/121427809