冒泡排序和插入排序的一点思考
2017-02-15 10:00
204 查看
冒泡排序和插入排序都是时间复杂度为O(n2)的排序算法,实现简单但是效率低下。这两个排序算法长相类似,初学者很容易弄混淆,今天我来简要分析二者区别。
冒泡排序大体思路:把一串元素排成一列,每次从最下面的元素开始,与其上面一个元素进行对比,然后把大元素移到上面(如果下面的元素更大),这样轮一次,就把整个序列中的最大元素放到了列顶,然后把第二大元素放到次顶,如此循环……总共比较次数为(n-1)*(n-2)……*1。
插入排序大体思路:把一串元素排成一列,让顶部的p个元素有序,然后把p+1号元素以冒泡的方式插入到p个元素中的合适位置,此时就得到顶部p+1个元素有序,随着p从1增大到n,就完成了整串元素有序。总的比较次数1*2*……(n-2)*(n-1),看起来和冒泡排序比较次数相同,但这是最差情况(整个序列是倒置的),在插入排序的冒泡过程中,一旦元素冒到了目标位置,就停止了后续比较(如果p+1比p小,那么肯定比p上面的所有元素都小,直接结束比较)。
个人观点:
所有的复杂度为O(n2)的排序算法,执行的比较次数都是(n-1)*(n-2)……*1这个量级(好像是废话)。
冒泡排序不折不扣的执行所有的比较操作,冒泡排序每次冒出的元素,以后都不会再变动(第x位的元素一定是整个序列中的第x大)。
插入排序在每轮比较操作中可以提前中止,因此获得了更高的效率(比较次数是冒泡的一半),这个效率提升是有代价的:已排序完成的子序列不是固定的,简单点说,插入排序中排好的有序子序列还有可能被改动(假如前x个元素有序,其顶部最大的元素只是该x个元素中最大的,如果第x+1个元素更大,在下一次操作中它会被冒到顶部),也就说插入排序在排序完成之前我们无法获得序列最大值。
在绝大多数情况下我们要的只是整个序列有序,排序过程中获得序列最大值没有意义。而一旦要求改为“获得序列中最大的n个数”,那么就只能用冒泡排序了。
那么冒泡排序的效率是不是就一定比插入排序差呢?答案是否定的。因为冒泡排序上来就整理整个序列,所以可能会出现第一轮遍历就把序列整理完成的情况(比如整理序列2,1,4,3,6,5)。因此冒泡排序有个优化版本:每轮遍历都记录该轮遍历是否有交换,如果没有则表示排序完成。这种提前感知排序完成的能力是插入排序无法做到的。
扩展思考(与选择排序比较):
冒泡排序和插入排序的比较操作很多,一个元素从底部冒到顶部的过程中要和中间的每个元素做交换。大量交换操作带来了效率上的浪费,而带来的好处就是排序是稳定的。
选择排序:每次遍历比较整个序列找到最大值,然后把它和当前的顶元素直接交换。因为找的是整个序列的最大值,所以比较次数无法优化(同冒泡),而交换次数最大为n-1(非常棒)。劣势:交换过于直接导致排序不稳定,需要一个额外空间在比较过程中记录临时最大值。
如果比较操作更费时,那么插入排序更好;如果交换操作更费时,那么选择排序更好。
冒泡排序大体思路:把一串元素排成一列,每次从最下面的元素开始,与其上面一个元素进行对比,然后把大元素移到上面(如果下面的元素更大),这样轮一次,就把整个序列中的最大元素放到了列顶,然后把第二大元素放到次顶,如此循环……总共比较次数为(n-1)*(n-2)……*1。
插入排序大体思路:把一串元素排成一列,让顶部的p个元素有序,然后把p+1号元素以冒泡的方式插入到p个元素中的合适位置,此时就得到顶部p+1个元素有序,随着p从1增大到n,就完成了整串元素有序。总的比较次数1*2*……(n-2)*(n-1),看起来和冒泡排序比较次数相同,但这是最差情况(整个序列是倒置的),在插入排序的冒泡过程中,一旦元素冒到了目标位置,就停止了后续比较(如果p+1比p小,那么肯定比p上面的所有元素都小,直接结束比较)。
个人观点:
所有的复杂度为O(n2)的排序算法,执行的比较次数都是(n-1)*(n-2)……*1这个量级(好像是废话)。
冒泡排序不折不扣的执行所有的比较操作,冒泡排序每次冒出的元素,以后都不会再变动(第x位的元素一定是整个序列中的第x大)。
插入排序在每轮比较操作中可以提前中止,因此获得了更高的效率(比较次数是冒泡的一半),这个效率提升是有代价的:已排序完成的子序列不是固定的,简单点说,插入排序中排好的有序子序列还有可能被改动(假如前x个元素有序,其顶部最大的元素只是该x个元素中最大的,如果第x+1个元素更大,在下一次操作中它会被冒到顶部),也就说插入排序在排序完成之前我们无法获得序列最大值。
在绝大多数情况下我们要的只是整个序列有序,排序过程中获得序列最大值没有意义。而一旦要求改为“获得序列中最大的n个数”,那么就只能用冒泡排序了。
那么冒泡排序的效率是不是就一定比插入排序差呢?答案是否定的。因为冒泡排序上来就整理整个序列,所以可能会出现第一轮遍历就把序列整理完成的情况(比如整理序列2,1,4,3,6,5)。因此冒泡排序有个优化版本:每轮遍历都记录该轮遍历是否有交换,如果没有则表示排序完成。这种提前感知排序完成的能力是插入排序无法做到的。
扩展思考(与选择排序比较):
冒泡排序和插入排序的比较操作很多,一个元素从底部冒到顶部的过程中要和中间的每个元素做交换。大量交换操作带来了效率上的浪费,而带来的好处就是排序是稳定的。
选择排序:每次遍历比较整个序列找到最大值,然后把它和当前的顶元素直接交换。因为找的是整个序列的最大值,所以比较次数无法优化(同冒泡),而交换次数最大为n-1(非常棒)。劣势:交换过于直接导致排序不稳定,需要一个额外空间在比较过程中记录临时最大值。
如果比较操作更费时,那么插入排序更好;如果交换操作更费时,那么选择排序更好。
相关文章推荐
- 用汇编与C实现冒泡排序以及一点思考
- 用汇编与C实现冒泡排序以及一点思考
- 关于MVC,MVP,MVVM的一点总结和思考
- 客户端技术的一点思考
- (一)Java实现排序,选择排序,快速排序,冒泡排序,插入排序
- 简单排序Java实现(一):冒泡排序,选择排序,插入排序(原理及实现)
- 如何分析思考oralce的一些问题的一点感悟!
- 关于语音合成的一点思考
- 未来之路:年底关于个人职业发展的一点思考-技术是不是青春饭?
- 排序(冒泡排序,插入排序,希尔排序,选择排序,堆排序)
- 对于表列数据类型选择的一点思考
- 数据结构-练习 11 冒泡排序 插入排序 归并排序
- 你的生命有什么可能?--职业生涯的一点思考
- 关于浏览器内存占用的一点思考(实际测试篇)
- 插入排序、冒泡排序以及它们的区别
- 对合并排序和快速排序的一点思考
- JAVA中排序算法(冒泡排序、选择排序、插入排序、快速排序)
- 约瑟夫问题(Josephus problem)的一点思考
- 关于样本方差以及样本协方差的一点思考
- Java基本排序(插入排序,冒泡排序,选择排序)