归并排序:
先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
先是自顶向下细分,再自底向上合并。复杂度相当于N乘以log(N)。
参考:https://blog.csdn.net/Linzhongyilisha/article/details/100110826
假设对 n 个元素进行归并排序需要的时间是 T(n),那分解成两个子数组排序的时间都是 T(n/2)。
merge()函数合并两个有序子数组的时间复杂度是 O(n)。
T(1) = C; n=1时,只需要常量级的执行时间,所以表示为C。
T(n) = 2*T(n/2) + n; n>1
= 2*(2*T(n/4) + n/2) + n
= 4*T(n/4) + 2*n
= 4*(2*T(n/8) + n/4) + 2*n
= 8*T(n/8) + 3*n
= 8*(2*T(n/16) + n/8) + 3*n
= 16*T(n/16) + 4*n
......
= 2^k * T(n/2^k) + k * n
当 T(n/2^k)=T(1) 时,也就是 n/2^k=1,我们得到 k=log2n 。我们将 k 值代入上面的公式,得到 T(n)=Cn+nlog2n 。如果我们用大 O 标记法来表示的话,T(n) 就等于 O(nlogn)。所以归并排序的时间复杂度是 O(nlogn)。
快速排序(Quicksort)
参考:https://blog.csdn.net/Linzhongyilisha/article/list/2
从中随机挑选一个,这个被随机选上的数字被称为枢值比如53,接下来,将所有要排序的数字分成两部分,第一部分是大于等于枢值53的,第二部分是小于枢值53的。
第二步,从上面得到的两堆数字,分别采用第一步的方法各自再找一个枢值。对于第一堆,由于所有的数字都比53大,至少也等于53,因此,第二次随机挑选的枢值肯定是一个大于53的数字,比如79;类似地,对于第二堆,由于所有的数字都小于53,因此第二次随机挑选的枢值肯定小于它,比如4。
接下来,再把两堆数字各自分成大于等于相应枢值的数字序列,以及小于枢值的数字序列。
这样做下来,原来的一大堆数就变成了四小堆,它们分别是小于4的数字,介于4到53之间的,介于53到79之间的,以及大于或等于79的。
再接下来,用同样的方法,四堆变八堆,八堆变十六堆,很快所有的数字就排好序了。
这种算法通常情况下复杂度也是N乘以log(N)
在工程上,快速排序算法一般情况下比归并排序快两倍,因此在工程上还是有意义的。
有没有可能发明一种比快速排序更好的算法?
从科学上讲,答案是否定的。因为从数学上可以证明N个任意随机数的排序,复杂度不可能比N乘以log(N)更低,
这是数学给出的极限,或者边界。
用快排思想在O(n)内查找第K大元素:
我们选择数组区间 A[0…n-1]的最后一个元素 A[n-1]作为 pivot,
对数组 A[0…n-1]原地分区,这样数组就分成了三部分,A[0…p-1]、A[p]、A[p+1…n-1]。
如果 p+1=K,那 A[p]就是要求解的元素;
如果 K>p+1, 说明第 K 大元素出现在 A[p+1…n-1]区间,我们再按照上面的思路递归地在 A[p+1…n-1]这个区间内查找。
如果 K<p+1,那我们就在 A[0…p-1]区间查找。
第一次分区查找,我们需要对大小为 n 的数组执行分区操作,需要遍历 n 个元素。
第二次分区查找,我们只需要对大小为 n/2 的数组执行分区操作,需要遍历 n/2 个元素。
依次类推,分区遍历元素的个数分别为、n/2、n/4、n/8、n/16.……直到区间缩小为 1。
如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。这是一个等比数列求和,最后的和等于2n-1。
所以,上述解决思路的时间复杂度就为 O(n)。