基础排序算法总结
2013-02-20 21:01
211 查看
首先要理解的几个概念:
1. 在原序列中,若Ri = Rj,且i<j,排序后Ri仍在Rj前面,则该排序算法为稳定排序,否则为非稳定排序。
2. 排序可分为:内排序(数据在内存中)和外排序(数据在外存中)。本文重点在一些内排序的思想。不贴代码和细节考虑。
例子如下(来自wiki):
当原序列有序时,冒泡排序出现最好情况O(N);当原序列逆序时,出现最坏情况O(N^2)。
枢纽元如何选取:
1. 选择第一个元素。如果是随机的,可以接受。但若原序是逆序的,此时会出现最坏情况
2. 随机选取
3. 采样,估计中值,样本越大,估算越准
平均情况下,时间性能为O(NlogN)。
时间复杂度:
T(1) = 1 ,
T(N) = 2T(N/2) + N
求解上述递推公式,得O(NlogN),此复杂度与初始序列无关。
缺点:
1. 从空间上,需要额外的空间
2. 从时间上,复制到临时数组再复制回来,耗时
假设要对{91,48,15,17,39,54}排序
首先以个位数字为索引分别入桶,变成以下状态:
然后将桶里的数字按序拿出来,得到{91,54,15,17,48,39}
再次以十位数为索引分别入桶,变成以下状态
然后将桶里的数字按序拿出来,得到{15,17,39,48,54,91}
因为没有一个数字有百位,所以排序结束;否则继续以百位为索引入桶。
可以看到这是由6片树叶构成的二叉树。树的深度就是需要比较的次数,也就是时间复杂度。
一般地,对N个元素进行排序,有N!种不同的排列,对应的决策树就有N!片树叶。我们需要根据这个信息来求出这棵树的深度。
先看几个引理:
引理1:令T是深度为d的二叉树,则T最多有2^d片树叶。
证明:数学归纳法。
引理2:具有L片树叶的二叉树的深度至少是ceil(logL)。 (ceil()是向上取整)
证明:由引理1得。
所以,根据引理2,N!片树叶的二叉树深度至少是log(N!) = O(NlogN)。
所有基于比较的排序算法都逃不开NlogN的命运。
参考资料:
[1] Data structures and Algorithm Analysis in C++. Mark Allen Weiss
[2] http://v.ku6.com/show/CyrKVsWot8xwAxcM2xpExQ...html 这个系列的排序舞蹈比较好玩
[3] http://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html 排序动画化
[4] http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html#2602332 这位同学列出了很多不常见的排序算法
1. 在原序列中,若Ri = Rj,且i<j,排序后Ri仍在Rj前面,则该排序算法为稳定排序,否则为非稳定排序。
2. 排序可分为:内排序(数据在内存中)和外排序(数据在外存中)。本文重点在一些内排序的思想。不贴代码和细节考虑。
1. 插入排序
基本思想:每次都将一个待排序的数据元素,按其关键字大小,插入到前面已排好序的一组数据元素中的适当位置上,直到所有数据插入完成。直接插入排序:
适用于少量数据元素的简单方法,复杂度O(N^2)例子如下(来自wiki):
二分插入排序:
在a[j]插入之前,a[0],a[1],...,a[j-1]已有序,可利用二分查找,快速找到插入位置,减少了比较次数。但没有减少数据的移动次数,仍然是O(n^2)希尔排序:
直接插入的改进版,效率比O(N^2)好,是较快算法中最简单的算法,通过减少移动次数来提高效率。希尔建议:间距从N/2开始平分直到1。该算法性能的数学分析是数学界的一大难题。2. 选择排序
基本思想:从n个中挑选最小的,从剩余n-1个中挑选最小的,反复直至排序完成。直接选择排序:
基于上述思想扫n遍数组,效率低,O(N^2)堆排序:
用二叉堆实现的优先级队列。原序列建堆(O(N)),N次deQueue(O(NlogN)),所以总的复杂度为O(NlogN),此复杂度与初始序列无关。把deQueue出来的元素放在数组的末尾,可以有效地避免用额外的空间。若想要非递减序列,则用最大化堆,反之亦然。3. 交换排序
基本思想:通过交换,键值大的向尾移动,键值小的向头移动。冒泡排序:
从头到尾比较相邻的两个元素,将小的换到前面,大的换到后面。一趟起泡使最大元素到尾,一共需要n-1趟比较。当原序列有序时,冒泡排序出现最好情况O(N);当原序列逆序时,出现最坏情况O(N^2)。
快速排序:
目前被认为最快的内排序。选取一个枢纽元,将原序列分为小于枢纽元的元素组成的的集合1,大于枢纽元的元素组成的集合2,再对这两个集合递归调用快速排序。枢纽元如何选取:
1. 选择第一个元素。如果是随机的,可以接受。但若原序是逆序的,此时会出现最坏情况
2. 随机选取
3. 采样,估计中值,样本越大,估算越准
平均情况下,时间性能为O(NlogN)。
4. 归并排序
基本思想:合并两个已排好序的列。顺序扫描两个列,将小的元素放到另一个空列里,直至完成。时间复杂度:
T(1) = 1 ,
T(N) = 2T(N/2) + N
求解上述递推公式,得O(NlogN),此复杂度与初始序列无关。
缺点:
1. 从空间上,需要额外的空间
2. 从时间上,复制到临时数组再复制回来,耗时
5. 桶排序(不基于比较)
基本思想:假设要对{5,3,7,1}排序,则维护一个大小为7的数组a,初始数组初值全赋为0。扫描输入,扫到x,则执行a[x] = 1。再扫一遍该数组,若a[x] = 1,则输出x。6. 基数排序(不基于比较)
基本思想:类似桶排序,但只有10个桶,具体例子见下:假设要对{91,48,15,17,39,54}排序
首先以个位数字为索引分别入桶,变成以下状态:
桶编号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
元素 | 91 | 54 | 15 | 17 | 48 | 39 |
再次以十位数为索引分别入桶,变成以下状态
桶编号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
元素 | 15,17 | 39 | 48 | 54 | 91 |
因为没有一个数字有百位,所以排序结束;否则继续以百位为索引入桶。
6. 间接排序
基本思想:如果要排序对象很大时,大量的复制和移动会降低效率。解决办法:生成一个指向排序对象的指针数组,然后重新排列这些指针。只有指针的移动和复制,避免了对象的直接移动。7. 排序算法的一般下界
基于比较的排序可以自然地表现为一棵决策树。下图为对a,b,c排序时的决策树:可以看到这是由6片树叶构成的二叉树。树的深度就是需要比较的次数,也就是时间复杂度。
一般地,对N个元素进行排序,有N!种不同的排列,对应的决策树就有N!片树叶。我们需要根据这个信息来求出这棵树的深度。
先看几个引理:
引理1:令T是深度为d的二叉树,则T最多有2^d片树叶。
证明:数学归纳法。
引理2:具有L片树叶的二叉树的深度至少是ceil(logL)。 (ceil()是向上取整)
证明:由引理1得。
所以,根据引理2,N!片树叶的二叉树深度至少是log(N!) = O(NlogN)。
所有基于比较的排序算法都逃不开NlogN的命运。
参考资料:
[1] Data structures and Algorithm Analysis in C++. Mark Allen Weiss
[2] http://v.ku6.com/show/CyrKVsWot8xwAxcM2xpExQ...html 这个系列的排序舞蹈比较好玩
[3] http://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html 排序动画化
[4] http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html#2602332 这位同学列出了很多不常见的排序算法
相关文章推荐
- python面试之算法基础(排序算法总结与实现)
- 基础排序算法总结
- 基础排序算法总结
- Java基础学习总结(28)——Java对各种排序算法的实现
- Java基础学习总结(28)——Java对各种排序算法的实现
- 基础排序算法总结
- Java基础学习总结(28)——Java对各种排序算法的实现
- Java基础学习总结(60)——Java常用的八种排序算法
- [Java]各种基础的查找和排序算法总结
- ZH奶酪:【数据结构与算法】基础排序算法总结与Python实现
- 基础排序算法总结(七种排序算法)
- Java基础学习总结(60)——Java常用的八种排序算法
- 算法基础:基本排序算法原理、实现与总结
- 基础排序算法总结
- 常见基础排序算法总结及java代码
- [数据结构]九大基础排序算法总结
- Java基础-几种常见排序算法总结
- 基础知识点总结:查找和排序算法
- 基础排序算法总结
- 基础排序算法总结及实现