排序算法是一个老生常谈的话题,也是面试中非常喜欢问的一个知识点,预计7月份更完面试中的10大排序算法(冒泡、选择、插入、希尔、堆、快速、归并、计数、基数、桶)。
一、冒泡排序的定义
如果你是以最大值为维度的排序算法,其实都可以叫冒泡排序。冒泡嘛,鱼吐泡,泡越来越大,新吐出来的泡也一定是最小的,映射到程序上就是每次得出array里的最大值并放到array的最后一项。
二、冒泡排序的重要性
是入门级的算法,也是排序算法里出现时间较早的一个算法。所以对于程序员来说,冒泡是基本功。
三、代码实践
比如我们有这样的一个数组A:[5, 8, 10, 3, 30, 20]。根据冒泡的思想,每次循环得出一个最大值。那对于数组A来说,就需要遍历6次才能得到一个从小到大的完整排序数组。
第一次遍历,有如下逻辑:
5跟8比较,5小于8,这次比较中,8是最大值,所以5跟8的位置不动,此时的数组:[5, 8, 10, 3, 30, 20];
8跟10比较,8小于10,这次比较中,10是最大值,所以8跟10的位置不动,此时的数组:[5, 8, 10, 3, 30, 20];
10跟3比较,10大于3,这次比较中,10是最大值,所以10需要跟3对换位置,此时的数组:[5, 8, 3, 10, 30, 20];
10跟30比较,10小于30,这次比较中,30是最大值,所以10跟30的位置不动,此时的数组:[5, 8, 3, 10, 30, 20];
30跟20比较,30大于20,这次比较中,30是最大值,所以30需要跟20的位置对调,此时的数组:[5, 8, 3, 10, 20, 30];
第一次遍历结束,我们得到了整个数组的最大值30,所以我们第二次循环的时候,最后一项就是20的位置。
第二次遍历,有如下逻辑:
// 第二次遍历开始的时候,数组A:[5, 8, 3, 10, 20, 30];
5跟8比较,5小于8,这次比较中,8是最大值,所以5跟8的位置不动,此时的数组:[5, 8, 3, 10, 20, 30];
8跟3比较,8大于3,这次比较中,8是最大值,所以8跟3的位置对调,此时的数组:[5, 3, 8, 10, 20, 30];
8跟10比较,8小于10,这次比较中,10是最大值,所以8跟10的位置不动,此时的数组:[5, 3, 8, 10, 20, 30];
10跟20比较,10小于20,这次比较中,20是最大值,所以10跟20的位置不动,此时的数组:[5, 3, 8, 10, 20, 30];
第二次遍历结束,我们得到了整个数组的第二大值,所以我们第三次循环的时候,最后一项就是10的位置
第N次循环,逻辑跟上面的一样,循环往复......
因此,根据上面的分析,我们可以得出如下冒泡代码:
function bubbling(arr){
let arrLength = arr.length;
let arr1 = [...arr];
// 最外围的遍历
for (let roundIndex = 0; roundIndex < arr1.length; roundIndex++){
// 在最外围的基础上,比较每个数据项
for (let dataIndex = 0; dataIndex < arrLength - roundIndex; dataIndex++){
if (arr1[dataIndex] > arr1[dataIndex + 1]){
// 交换位置
[ arr1[dataIndex], arr1[dataIndex + 1] ] = [ arr1[dataIndex + 1], arr1[dataIndex] ];
}
}
}
return arr1;
}
四、最后
好啦,本次冒泡到这里就结束啦,其实网上有很多关于冒泡的优化,但是在我看来治标不治本,因为他们的算法在遇到最坏的情况下,跟没优化的代码是一样的(其实主要还是看应用场景,毕竟场景不同,数据的表现形式千变万化),都要走那么多步骤,当然你们也可以去了解一下。
下期我会讲一下冒泡的逆向实现(也就是选择排序),我们下次再见啦~~