冒泡排序和插入排序都是时间复杂度为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(非常棒)。劣势:交换过于直接导致排序不稳定,需要一个额外空间在比较过程中记录临时最大值。
如果比较操作更费时,那么插入排序更好;如果交换操作更费时,那么选择排序更好。