归并排序:
归并排序。可以说分治策略用在了排序问题上。
我们每次将数组分成两半。递归的排序这两半。然后用线性时间将其合并起来。变成一个完整的有序数组。
复杂度分析:
T(n)=2T(n2)+O(n)
显然最多展开log2n层。所以总时间复杂度:O(nlogn)
堆排序:
如果不知道堆这个数据结构的同学。可以自行学习。(百度一大把)
通过不停到调整堆来做到排序。
每次将堆顶最小元素与堆最中后一个元素交换。类似于把最后一个元素从新入堆。
这一步从新调整耗费O(log2n)
排序n个元素复杂度:O(nlogn)
快速排序
正如其名。快速排序。当然是非常快的。
也是最重要。最需要学习的排序。
快速排序为何如此之快?
快速排序与归并排序类似。
不同的是:快速排序是先划分。不需要合并。
归并排序最慢的部分。就是需要把数组归并到一个辅助数组中。在把排序到辅助数组中的元素从新拷贝到原来数组对应位置。
快速排序不同:
最经典的快速排序是随机的选择一个数列中的元素a
然后将这n个元素的序列划分为:
[不大于a的部分] a [不小于a的部分]
具体实现技巧在于。
我们随机的选择a=A[i].
将A[i] 与 A[0]交换位置。
令t=0,k=n−1
我们认为:A[0..t−1]所有元素小于等于a
我们认为:A[k+1..n−1]所有元素大于等于a
开始时, a在 A[t] 位置。
循环:检查A[k]是否小于A[t]。
如果不是k=k−1,继续检查。直到k等于t.此时划分完成。
如果是 : 将A[k]归入小于等于a的集合。即:交换A[t],A[k]。
检查A[k]是否小于A[t]
如果不是, t=t+1.直到k等于t,划分结束。
如果是。交换A[k],A[t],返回循环的位置。
通过这样的划分。我们将序列划分成了不大于a的部分和不小于a的部分。
记a现在的位置为mid.显然。a的位置不需要改变了。
我们只需要递归的排序:
sort(0,mid−1)和sort(mid+1,n−1)
快速排序的效率难免有些许不稳定。因为它的速度与它的划分是否得当有关。
但实践表明。总是出现坏的情况的划分是几乎不可能能的。快速排序的速度要比归并排序和堆排序都要快。
我们计算一下平均意义下的快速排序的效率:
T(n)=1n∑k=1n(T(n−k)+T(k−1))+cn=2n∑k=0n−1T(k)+cn
cn是划分n个元素时需要的时间。且a不需要排序。所以T(k−1)+T(n−k)
所以:
nT(n)=2∑k=0n−1T(n)+cn2
则n−1时:
(n−1)T(n−1)=2∑k=1n−2T(k)+c(n−1)2
两式子做差有:
nT(n)−(n−1)T(n−1)=2T(n−1)+2cn−1nT(n)=(n+1)T(n)+2cn−1
哇,两边除去n(n+1):
T(n)n+1=T(n−1)n+2cn+1−1n(n+1)
令:
S(n)=T(n)n+1 , n≥0
所以:
S(n)=S(n−1)+2cn+1−1n(n+1)
所以:
S(n)=∑k=1n2ck+1−∑k=1n1k(k+1)
啊哈。有经验的人必然会发现:
1k(k+1)=1k−1k+1
所以:
S(n)=∑k=1n2ck+1−∑k=1n1k+∑k=1n1k+1=2c∑k=2n+11k−nn+1
也就是说:
T(n)n+1=2c∑k=2n+11k−nn+1T(n)=2c(n+1)∑k=2n1k+1−n
即:
T(n)=2cnHn+Hn−(2c+1)n−2c+1
其中
Hn=11+12+13+...+1n
则
Hn≤O(∫n11x dx)=O(logen)
所以:
T(n)=O(nlogn)
因为排序思想都比较简单。所以也就不再给出关于这三种排序的代码实现了.
在很多复杂度分析和算法思想中都可以找到这三种算法的影子。