排序算法
2016-02-05 23:41
239 查看
前言:我们常用的排序算法有插入排序(直接插入排序、希尔排序)、选择排序(简单选择排序、堆排序)、交换排序(冒泡排序、快速排序)、归并排序、基数排序。
比如有57 68 59 52四个数,将它们进行排序:
第一步:首先排序57,就一个元素,不用比较;
第二步:然后68和57进行比较,大于57,所以插在57的后边;
第三步:插入59,将59和57 68比较,插在57和68中间;
第四步:将52和数字比较,小于57,插在57前边。
例如:
第一步:首先求出增量,一般首次增量都为元素总数n的一半,图中10个元素,所以首次增量为5。然后相差5的每两个元素结为一组,进行比较,小的放到前面,就得到了图中第二列的排序;
第二步:将上次的增量的一半作为这次的增量,继续比较,得出第三列的排序;
第三步:依然按上次增量的一半分组,继续比较。。。知道增量为1;
第四步:增量为1并比较完后,将所有元素进行一次直接插入排序。
因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
第一步:从四个数中选择最小的排在最前边,即52放在第一个,将57放在原先52的位置;
第二步:从剩余三个数中选择最小的放在第二位,即57放在第二位,将68放在原先57的位置;
以此类推,得到一个从小到大的序列。
可以用一个二叉树画出来:
可以看出是一个完全二叉树,如果是第一种情况Ki<=K2i且Ki<=K(2i+1),则是每个父节点都要小于子节点,称为小顶锥;
如果是第二种情况,说明每个父节点都大于子节点,叫做大顶锥。
我们每次取出根节点,然后将剩余的节点重新生成一个树,然后再取出新树的根节点放在上一个根节点的前边,这些根节点的排序就是一个堆排序。
例如:对数列{ 46 79 56 38 40 84 }建立大顶锥。
第一步:对这些数据建立完全二叉树,按层次遍历将数据一一填入,得到:
第二步:接下来将树进行调整为堆,因为叶子节点的左右指针为空不能找到下一个元素,所以从非叶子节点进行调整,我们选择n/2,即56所在的3号节点,发现它比其子节点84小,互换位置,得到:
第三步:调整n/2 -1节点,即2号节点,79比子节点都大,不用调整;
第四步:继续减1调整,即1号节点,将46和它两个子节点中较大的一个对调,得到:
因为46比子节点56小,所以继续和56对调,得到:
这就是一个初始大顶堆了。
接下来得到堆排序:
首先取出根节点84,具体方式是将84与最后一个节点对调,然后断开最后一个节点和父节点的连接,就取出了84:
继续对取出84后的树进行调整,得到:
然后将79和最后一个节点对调,取出79,放在84的前边:
以此类推,对于最终全部取出的元素得到一个从小到大的堆序列,即:38 40 46 5679 84 。
第一步:将最后一个和倒数第二个进行比较,较小的放前边,然后倒数第二个和倒数第三个进行比较,较小的放前边。。。一直比到第一个元素。
这时候,第一个元素必然是所有元素中最小的一个。
第二步:仍然将最后一个和倒数第二个进行比较。。。此时比较到第二个元素就可以,因为第一个已经是最小的,所以每一次整体比较都能少比较一次。
第三步:重复以上步骤,最终得到从小到大的序列。
然后再除基准外的两个元素集合中再次找基准元素进行此样排序,最终得到一个从大到小的元素序列。
基准元素一般选择第一个或者最后一个元素。
第一步:左指针指向57,右指针指向19,19比57小,与57互换位置;
第二步:左指针向右移动一位,使68和57比较,比57大,互换位置;
第三步:右指针向左移动一位,24比57小,互换位置;
以此类推,直到左右指针交叉结束,就将元素分成了三部分。接下来对非基准元素集分别再排序即可。
第一步:首先将待排序元素每两个分为一组,组内小的放在前边,大的在后;
第二步:每两组合并为一组,进行排序,直到所有元素都合并为一个大组,就得到了最终排序。
如果比较过程中出现基数相同的时候,就往下挂:
其中有两个7,红色在前,紫色在后,如排序结束后,对两个7的排列仍为红色在前、紫色在后,那么我们就称之为稳定的排序算法;否则,称为不稳定的排序算法。
附:各类排序算法的比较:
1、插入排序
(1)直接插入排序
直接插入排序(InsertionSort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。比如有57 68 59 52四个数,将它们进行排序:
第一步:首先排序57,就一个元素,不用比较;
第二步:然后68和57进行比较,大于57,所以插在57的后边;
第三步:插入59,将59和57 68比较,插在57和68中间;
第四步:将52和数字比较,小于57,插在57前边。
(2)希尔排序
希尔排序又称为缩小增量排序,该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。例如:
第一步:首先求出增量,一般首次增量都为元素总数n的一半,图中10个元素,所以首次增量为5。然后相差5的每两个元素结为一组,进行比较,小的放到前面,就得到了图中第二列的排序;
第二步:将上次的增量的一半作为这次的增量,继续比较,得出第三列的排序;
第三步:依然按上次增量的一半分组,继续比较。。。知道增量为1;
第四步:增量为1并比较完后,将所有元素进行一次直接插入排序。
因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
2、选择排序
(1)简单选择排序
排出的结果是一个从小到大的序列,例图:
第二步:从剩余三个数中选择最小的放在第二位,即57放在第二位,将68放在原先57的位置;
以此类推,得到一个从小到大的序列。
(2)堆排序
堆的定义:n个元素的序列{k1,k2,。。。kn}当满足如下关系,称为堆:可以用一个二叉树画出来:
可以看出是一个完全二叉树,如果是第一种情况Ki<=K2i且Ki<=K(2i+1),则是每个父节点都要小于子节点,称为小顶锥;
如果是第二种情况,说明每个父节点都大于子节点,叫做大顶锥。
我们每次取出根节点,然后将剩余的节点重新生成一个树,然后再取出新树的根节点放在上一个根节点的前边,这些根节点的排序就是一个堆排序。
例如:对数列{ 46 79 56 38 40 84 }建立大顶锥。
第一步:对这些数据建立完全二叉树,按层次遍历将数据一一填入,得到:
第二步:接下来将树进行调整为堆,因为叶子节点的左右指针为空不能找到下一个元素,所以从非叶子节点进行调整,我们选择n/2,即56所在的3号节点,发现它比其子节点84小,互换位置,得到:
第三步:调整n/2 -1节点,即2号节点,79比子节点都大,不用调整;
第四步:继续减1调整,即1号节点,将46和它两个子节点中较大的一个对调,得到:
因为46比子节点56小,所以继续和56对调,得到:
这就是一个初始大顶堆了。
接下来得到堆排序:
首先取出根节点84,具体方式是将84与最后一个节点对调,然后断开最后一个节点和父节点的连接,就取出了84:
继续对取出84后的树进行调整,得到:
然后将79和最后一个节点对调,取出79,放在84的前边:
以此类推,对于最终全部取出的元素得到一个从小到大的堆序列,即:38 40 46 5679 84 。
3、交换排序
(1)冒泡排序
每次比较出一个最小的元素放到前边,最终得到一个从小到大的序列,例如:第一步:将最后一个和倒数第二个进行比较,较小的放前边,然后倒数第二个和倒数第三个进行比较,较小的放前边。。。一直比到第一个元素。
这时候,第一个元素必然是所有元素中最小的一个。
第二步:仍然将最后一个和倒数第二个进行比较。。。此时比较到第二个元素就可以,因为第一个已经是最小的,所以每一次整体比较都能少比较一次。
第三步:重复以上步骤,最终得到从小到大的序列。
(2)快速排序
快速排序体现的是一种分治法,首先选择一个基准元素,结合左右指针将基准元素与剩余元素进行比较,比基准元素小的放在基准之前;比基准元素大的都放在基准之后。从而待排序元素分成三部分,比基准小的元素、基准元素和比基准大的元素。然后再除基准外的两个元素集合中再次找基准元素进行此样排序,最终得到一个从大到小的元素序列。
基准元素一般选择第一个或者最后一个元素。
第一步:左指针指向57,右指针指向19,19比57小,与57互换位置;
第二步:左指针向右移动一位,使68和57比较,比57大,互换位置;
第三步:右指针向左移动一位,24比57小,互换位置;
以此类推,直到左右指针交叉结束,就将元素分成了三部分。接下来对非基准元素集分别再排序即可。
4、归并排序
顾名思义,归并排序是使用合并操作完成排序的算法,如利用二路合并操作称为二路合并操作:第一步:首先将待排序元素每两个分为一组,组内小的放在前边,大的在后;
第二步:每两组合并为一组,进行排序,直到所有元素都合并为一个大组,就得到了最终排序。
5、基数排序
基数排序是比较有意思一种排序方法,我们这里依次比较个位、十位、百位上的基数,每比较一轮,都得出一个新的排序,当全部比较结束后,排序也就完成了。如果比较过程中出现基数相同的时候,就往下挂:
6、排序算法总结
排序算法的稳定性是指对相同关键字的排序不会变,例如对于待排序数列:其中有两个7,红色在前,紫色在后,如排序结束后,对两个7的排列仍为红色在前、紫色在后,那么我们就称之为稳定的排序算法;否则,称为不稳定的排序算法。
附:各类排序算法的比较:
相关文章推荐
- AA树
- redis设置为系统服务并开机启动(附脚本文件)
- java.lang.NoSuchMethodException: setContentView [int] android ViewUtils
- 如何使用struts2的Interceptor
- python成长之路第一篇(5)文件的基本操作
- java 通过zxing生成二维码
- [Algorithm]Maze Prim算法与A*寻路算法(上)
- LightOJ 1035 - Intelligent Factorial Factorization (求因子)
- 跳跃表实现的原理
- 日元负利率和美元暴跌,是对人民币的夹击
- 8 种常用的 NoSQL 数据库系统对比分析
- Android与PC间进行Socket通信
- Mybatis typeAliases别名
- LeetCode 73. Set Matrix Zeroes
- 利用github搭建个人小站
- php判断ip是否在指定ip区间内
- Linux 文件I/O — 文件描述符详解
- jQuery8(常见方法next.prev等,常见方法练习)
- 做得刚好
- Mybatis 实现手机管理系统的持久化数据访问层