码迷,mamicode.com
首页 > 编程语言 > 详细

排序算法

时间:2016-04-16 18:19:03      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:

http://blog.csdn.net/xiazdong/article/details/8462393
 
 
In-place sort(不占用额外内存或占用常数的内存):插入排序、选择排序、冒泡排序、堆排序、快速排序。
Out-place sort:归并排序、计数排序、基数排序、桶排序。
 
stable sort:插入排序、冒泡排序、归并排序、计数排序、基数排序、桶排序。
unstable sort:选择排序、快速排序、堆排序。
 
如果对于不稳定的算法进行改进,使得那些不稳定的算法也稳定?
其实很简单,只需要在每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。

 

1. 插入排序

特点:stable sort、In-place sort
最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。
最差复杂度:当输入数组为倒序时,复杂度为O(n^2)。
插入排序比较适合用于“少量元素的数组”。
 
其实插入排序的复杂度和逆序对的个数一样,当数组倒序时,逆序对的个数为n(n-1)/2,因此插入排序复杂度为O(n^2)。
 
是否能将伪代码第6-8行用二分法实现?
实际上是不能的。因为第6-8行并不是单纯的线性查找,而是还要移出一个空位让A[i]插入,因此就算二分查找用O(lgn)查到了插入的位置,但是还是要用O(n)的时间移出一个空位。
 
快速排序(不使用随机化)是否一定比插入排序快?
答:不一定,当输入数组已经排好序时,插入排序需要O(n)时间,而快速排序需要O(n^2)时间。
 
伪代码:
技术分享
递归版:
技术分享
 
2. 冒泡排序
特点:stable sort、In-place sort
思想:通过两两交换,像水中的泡泡一样,小的先冒出来,大的后冒出来。
最坏运行时间:O(n^2)
最佳运行时间:O(n^2)(当然,也可以进行改进使得最佳运行时间为O(n))
 
冒泡排序和插入排序哪个更快?
一般的人回答:“差不多吧,因为渐近时间都是O(n^2)”。
但是事实上不是这样的,插入排序的速度直接是逆序对的个数,而冒泡排序中执行“交换“的次数是逆序对的个数,“比较”的次数可能更多,因此冒泡排序执行的时间至少是逆序对的个数,因此插入排序的执行时间至少比冒泡排序快。
 
伪代码:
技术分享
递归版:
技术分享
改进版:加flag控制
最佳运行时间:O(n)
最坏运行时间:O(n^2)
技术分享
 
3. 选择排序
特性:In-place sort,unstable sort。
思想:每次找一个最小值。
最好情况时间:O(n^2)。
最坏情况时间:O(n^2)。
 
为什么伪代码中第3行只有循环n-1次而不是n次?
在循环不变式证明中也提到了,如果A[1...n-1]已排序,且包含了A中最小的n-1个元素,则A[n]肯定是最大的,因此肯定是已排序的。
 
伪代码:
技术分享
递归版:
T(n)=T(n-1)+O(n) => T(n)=O(n^2)
技术分享
 
4. 归并排序
特点:stable sort、Out-place sort
思想:运用分治法思想解决排序问题。
最坏情况运行时间:O(nlgn)
最佳运行时间:O(nlgn)
 
分治法介绍:分治法就是将原问题分解为多个独立的子问题,且这些子问题的形式和原问题相似,只是规模上减少了,求解完子问题后合并结果构成原问题的解。
分治法通常有3步:Divide(分解子问题的步骤) 、 Conquer(递归解决子问题的步骤)、 Combine(子问题解求出来后合并成原问题解的步骤)。
假设Divide需要f(n)时间,Conquer分解为b个子问题,且子问题大小为a,Combine需要g(n)时间,则递归式为:
T(n)=bT(n/a)+f(n)+g(n)
就如归并排序,Divide的步骤为m=(p+q)/2,因此为O(1),Combine步骤为merge()函数,Conquer步骤为分解为2个子问题,子问题大小为n/2,因此:
归并排序的递归式:T(n)=2T(n/2)+O(n)
 
而求解递归式的三种方法有:
(1)替换法:主要用于验证递归式的复杂度。
(2)递归树:能够大致估算递归式的复杂度,估算完后可以用替换法验证。
(3)主定理:用于解一些常见的递归式。
 
归并排序的缺点是什么?
答:他是Out-place sort,因此相比快排,需要很多额外的空间。
 
问:为什么归并排序比快速排序慢?
答:虽然渐近复杂度一样,但是归并排序的系数比快排大。
 
问:对于归并排序有什么改进?
答:就是在数组长度为k时,用插入排序,因为插入排序适合对小数组排序。复杂度为O(nk+nlg(n/k)) ,当k=O(lgn)时,复杂度为O(nlgn)
 
伪代码:
技术分享
 
5. 快速排序
特性:unstable sort、In-place sort。
最坏运行时间:当输入数组已排序时,时间为O(n^2),当然可以通过随机化来改进(shuffle array 或者 randomized select pivot),使得期望运行时间为O(nlgn)。
最佳运行时间:O(nlgn)
快速排序的思想也是分治法。
当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都为O(n^2)。
 
注意:只要partition的划分比例是常数的,则快排的效率就是O(nlgn),比如当partition的划分比例为10000:1时(足够不平衡了),快排的效率还是O(nlgn)

 

伪代码:

技术分享

随机化partition的实现:
技术分享
技术分享
 
改进当所有元素相同时的效率的Partition实现:
技术分享
技术分享
 
改进方法应该是采取三路划分方法,将小于枢轴元素的元素放到数组最左边,将等于枢轴元素的元素放到数组中间,将大于枢轴元素的元素放到数组最右边。下次只处理左边部分和右边部分,中间部分不再参与下一轮排序。
这个方法不能完全满足只扫描一次的要求,但它有两个好处:首先,如果数据中没有重复的值,那么该方法几乎没有额外的开销;其次,如果有重复值,那么这些重复的值不会参与下一趟排序,减少了无用的划分。
 
6. 堆排序
特性:unstable sort、In-place sort。
最优时间:O(nlgn)
最差时间:O(nlgn)
思想:运用了最小堆、最大堆这个数据结构,而堆还能用于构建优先队列。
 
优先队列应用于进程间调度、任务调度等。
堆数据结构应用于Dijkstra、Prim算法。
 
伪代码:
技术分享
 
7. 计数排序
特性:stable sort、out-place sort。
最坏情况运行时间:O(n+k)
最好情况运行时间:O(n+k)
当k=O(n)时,计数排序时间为O(n)
 
伪代码: 
技术分享

 

8. 基数排序

本文假定每位的排序是计数排序。
特性:stable sort、Out-place sort。
最坏情况运行时间:O((n+k)d)
最好情况运行时间:O((n+k)d)
d为位数,k为基数(种类数),n为元素个数。
当d为常数,k=O(n)时,效率为O(n)。
 
我们也不一定要一位一位排序,我们可以多位多位排序,比如一共10位,我们可以先对低5位排序,再对高5位排序。
引理:假设n个b位数,将b位数分为多个单元,且每个单元为r位,那么基数排序的效率为O[(b/r)(n+2^r)]。
当b=O(nlgn),r=lgn时,基数排序效率O(n)
例:说明如何在O(n)时间内,对0~n^2-1之间的n个整数排序?
答案:将这些数化为2进制,位数为lg(n^2)=2lgn=O(lgn),因此利用引理,b=O(lgn),而我们设r=lgn,则基数排序可以在O(n)内排序。
 
基数排序的例子:
技术分享
技术分享
 
技术分享
 
9. 桶排序
假设输入数组的元素都在[0,1)之间。
特性:out-place sort、stable sort。
最坏情况运行时间:当分布不均匀时,全部元素都分到一个桶中,则O(n^2),当然也可以将插入排序换成堆排序、快速排序等,这样最坏情况就是O(nlgn)。
最好情况运行时间:O(n)
 
桶排序的例子:
技术分享
技术分享
 
伪代码:
技术分享
 
技术分享

 

排序算法

标签:

原文地址:http://www.cnblogs.com/argenbarbie/p/5398863.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!