一、引言
今天学了一个快速选择算法,这个算法跟快排差不多,只不过专门用来在选择数组中第k大/小的数时,可以免去将整个数组排序后选择第k个数。将整个数组排序再选择第k个数的时间复杂度是O(nlog(n)),快速选择算法将平均时间复杂度优化到O(n),但最坏的情况还是O(n^2)。一开始很纠结这个O(n)是怎么来的,后来看了一下别人的分析,得到了答案。
二、分析
这里大概分析一下O(n)怎么来的,可能不是很严谨。
(1)快速选择算法,每次根据pivot(枢纽值)将数组分成左部分和右部分以后,根据k值的大小选择下一次进入左部分还是右部分,我们把数组分成左右部分的过程叫partition。
(2)假设用随机选择算法取得的枢纽值总是在数组的中间,那每次都会将数组分成等长(或长度相差1)的两半,再假设如果一直到最后子数组长度为1才找到第k大/小的值,那就一共要进行⌊logn⌋+1次partition。
(3)第一次partition要遍历的长度是n,第二次要遍历的长度是n/2,第三次要遍历的长度是n/4……倒数第二次遍历的长度是2,最后一次遍历的长度是1。这是一个首项为1,公比为2的等比数列,记ax=2^(x-1),其中x为项数。这个等比数列的前x项和为2^x-1(等比数列的求和公式为Sx=(a1-ax*q)/(1-q))。因为一共要求⌊logn⌋+1次partition,而S(⌊logn⌋+1)=2^(⌊logn⌋+1)-1≈2n-1(大概是这样,会有误差,但主要是看数量级。数学这方便确实比较菜嘻嘻),所以时间复杂度是O(2n-1)=O(n)。
三、总结
一开始觉得快速选择算法的平均时间复杂度应该跟快排一样是O(nlogn),因为觉得要进行⌊logn⌋+1次的partition,每次最差都要遍历长度为n的数组(被以前那些O(n^2)分析带偏了)。实际上每次partition平均来说只要遍历n/2的长度。时间复杂度分析不能想当然,还是要用数学的方式推导一下。