今天要介绍的是另一种排序算法,冒泡排序。冒泡排序和快速排序一样,都是属于交换排序,也就是都是通过判断某种条件,对数组进行交换操作,实现排序的算法。
对很多计算机专业的童鞋来说,冒泡排序算法应该是他们上在大学课堂上接触的第一种排序算法。大多数高校的计算机专业的第一门程序设计课应该都是C语言,我还记得是讲完流程控制语句和循环语句之后书中的一个例子介绍的就是冒泡排序算法。因为冒泡算法就和它的名字一样,非常的形象,排序过程很好理解,也不像快速排序那样,实现起来需要调用数组和日期的方法,以及递归的思想,掌握起来不那么容易。冒泡排序只需要简单逻辑判断语句以及循环语句就可以轻松实现。纵观八种排序算法,冒泡排序应该是最容易实现最容易理解的排序算法。
虽然冒泡排序的实现很简单,但是在日常开发中很少使用,因为它的平均时间复杂度为O(n2),这个和快速排序的nlg(n)时间复杂度相差太多,如果待排序的数组是千万级的上亿级的数据规模,那么进行一次的冒泡排序比快速排序多耗费的时间无疑是巨大的。不管对C端还是B端的用户来说,过长的时间查询都是极差的用户体验,如果在淘宝上输入一个搜索条件,几秒钟都没有出结果我相信可能很少有人会选择继续等待。因此对网站来说,效率就是生命,时间就是金钱。
即使冒泡排序的效率低,耗费的时间长,但是任何东西都有正反两面,世界上一切的存在都是合理的,再优秀的人也有弱点,再平庸的人也有擅长的一面。而冒泡排序也不例外,它是一个稳定的排序算法,而快速排序算法是不稳定的排序算法。在排序中的所谓的稳定性指的是,如果在数组中两个值相等,则在排序之后不会改变这两个值的位置,这个是快速排序不具备的,如果两个元素相等的时候,冒泡排序不会进行交换,但是快速排序仅仅通过基准进行判断,在有些情况下会对相等的元素进行交换。废话不多说了,下面开始介绍冒泡排序的算法和实现。
上一篇博客我把快速排序归纳为两个步骤,那么以简单著称的冒泡排序只需要用一步就可以实现:
1、从数组的第一个开始两两比较,如果前面的元素大于后面的元素,两个元素交换。
2、忽略最后一个元素,重复第一步。
虽然这两句话看起来简单明了,但是我还是要举一个例子,形象的解释一下这个过程:
待排序数组:[3,2,1];
第一遍处理:[2,3,1]——>[2,1,3]
第二遍处理:[1,2,3]
可以看见,第一遍处理的时候,3和2进行比较,3比2大,3和2交换,然后3和1进行比较,3大于1,3和1进行交换。这时候,数组中最大的元素,被移动到了最后一位,这就是第一遍的效果,而第二遍,2和1进行比较,2比1大,2和1进行交换,然后由于最大的就是3,不需要交换。这样第二大的元素被放到了倒数第二位,还剩一个1自然就是最小的元素。整个排序的过程就和一个气泡从一个试管里浮上来,而最大的元素就是那个气泡,其他的元素构成了这个试管。我觉得有的时候,千言万语都赶不上一段代码来的实在,这也许就是说和做的区别,接下来我就来介绍一下冒泡排序算法的代码实现。
首先定义一个排序函数,接收一个待排序的数组作为参数,如果数组的长度小于二直接将数组返回。
function bubbleSort(arr) {
if(arr.length<2){
return arr;
}
}
准备工作做完下面进行第一步:从数组的第一个开始两两比较,如果前面的元素大于后面的元素,两个元素交换和第二步:忽略最后一个元素,重复第一步。
这一步中需要解释的有两个部分,首先需要解释的是两个元素进行交换的操作,原理很简单,定义一个空的元素,用来做两个元素的中间变量,就好比现在有两个球,分别在你的左手和右手,要将这两个球从你的双手交换位置,但是一个手只允许放一个球,这个时候你需要第三个容器先把左手的球放到容器里,再把右手的球放到左手里,然后用右手拿起容器里面的球,这样就实现了左右手球的交换操作。
接下来是嵌套的for循环,刚才介绍算法的时候,举的例子[3,2,1],第一遍是两次交换,第二遍是一次交换。第三遍是0次交换。在程序中,外层循环控制的就是排序算法的操作的遍数,而内层循环控制的才是真正每遍循环需要进行交换操作的次数,通过简单的观察可以看出这样的规律,每次交换的次数=数组的长度-第几遍,在两次for循环之后就得到了排序之后的数组。虽然解释的不那么标准和官方,但是真的懂了就是最好的。接下来是代码实现,这个时候不得不小小的吐槽一下简书的富文本编辑器,在这上面敲代码好难,还是截图吧。
虽然在代码量上并没有比快速排序少很多,但是算法的复杂度上,实现的难度上确实简单很多。
关于冒泡排序的算法就介绍到这,接下来聊点题外话。接下来聊点我的一点小想法,可能不太成熟,希望前辈大神们批评指正,在上面我举的第一个例子,[3,2,1],数组的长度为3,第一遍处理进行了两次交换操作,第二遍处理进行了一次交换操作。一共1+2次交换。试想一下,如果数组的长度为4,一共会进行3+2+1次交换操作。就在写这篇博客的时候,看到了这两串数字,就让我想起了高中学过的数学知识,等差数列,每次的交换次数恰恰是等差数列的求和公式,
在算法中,假设n为无穷大,n在n2面前可以忽略不计所以n+n2就是n2,而1/2*n2也就是n2,因此,可以得出如果要进行冒泡排序最坏的情况下需要进行n2次交换操作,所以时间复杂度为O(n2)。这也许是比较另类的冒泡排序算法的时间复杂度的推导方式。
写完了两篇博客,最大的感受是觉得自己收获了真的很多,渐渐发现,分享真的是很美好的,在写博客的过程中会弥补很多自己的知识漏洞,提升自己,在写完第一篇快速排序算法的当天晚上,连做梦都在写那一段代码。把一件事情讲明白,自己也就很透彻的明白了,比读书,查资料真的要高效的多。如果在博客中有错误,或者不足都希望前辈们批评指正。