本系列的文章列表和相关说明,请查看【一块儿学习排序算法】0 序言
也能够直接到github上查看完整的文章和源码!javascript
先看看Wikipedia的定义:java
Bubble sort is a simple sorting algorithm that repeatedly steps through the list to be sorted, compares each pair of adjacent items and swaps them if they are in the wrong order. The pass through the list is repeated until no swaps are needed, which indicates that the list is sorted.git
因此冒泡排序就是,每次比较相邻的两个元素,若是顺序不对,则交换元素。每一次迭代,最大的元素会排到列表的一侧。而后重复这个步骤,直到不须要交换元素,该数组即有序了。github
能够经过动画演示理解, 如下网上找的两个动画。若是你想操做不一样的参数来演示,能够上这个网站visualgo.net动手试试。算法
关于代码,README中代码只有实现算法的函数,具体运行的代码,请查看该目录下的文件。数组
代码以下, 看bubble_sort_1.js。app
const bubbleSort = (array) => {
// 不修改原数组
const originValues = array.slice();
// 迭代次数 数组长度-1
for (let i = 0; i < originValues.length - 1; i++) {
// 两两比较,该迭代的最大数,移动到右侧相应位置
for (let j = 0; j < originValues.length - 1 - i; j++) {
// 若是前一个数,大于后一个数,交换
if (originValues[j] > originValues[j + 1]) {
const tmp = originValues[j];
originValues[j] = originValues[j + 1];
originValues[j + 1] = tmp;
}
}
}
return originValues;
};
复制代码
代码其实已经很明显了。最外层循环控制迭代次数,内层循环两两比较,把较大的数往右移动。不过有几点要提一下:ide
(length-1)
length
次,其实最后一次多余了,由于只要前 n-1 都排序以后,第一个数确定是最小的数了。length-1-i
。复杂度分析:
很明显,无论数组正序仍是逆序,复杂度都是O(n2),因此最优复杂度和最坏复杂度都是O(n2)。 可资料上都是说,冒泡排序的最优复杂度是O(n)啊,那上面这种实现,确定能够优化。
仔细复盘上面的流程发现: 若是数组正序,比较一轮数组以后,后面还会傻傻地重复进行比较。而这实际上是没有必要的。只要在某一轮比较中,没有发生元素互换,就能够确认数组已经有序了。函数
代码以下, 看bubble_sort_2.js。post
const bubbleSort = (array) => {
// 不修改原数组
const originValues = array.slice();
let swapped = true;
do {
// 标记该次迭代是否交换顺序,若是没有,表明列表已经有序
swapped = false;
for (let i = 0; i < originValues.length - 1; i++) {
// 若是前一元素大于后一元素,交换顺序
if (originValues[i] > originValues[i + 1]) {
const tmp = originValues[i];
originValues[i] = originValues[i + 1];
originValues[i + 1] = tmp;
// 若是有交换,标记继续下一轮
swapped = true;
}
}
} while (swapped);
return originValues;
};
复制代码
复杂度分析:
通过上面的改造:当数组正序时,也就是最优复杂度达到了O(n);当数组逆序时,为最坏复杂度,为O(n2)。
咋一看,貌似是最终解了。但复盘以后,发现每轮已经排序的元素,还会重复去比较。因此还能够小优化一下。
代码以下, 看bubble_sort_3.js。
const bubbleSort = (array) => {
// 不修改原数组
const originValues = array.slice();
let swapped = true;
let hasOrderedCnt = 0; // 已排序个数
do {
// 标记该次迭代是否交换顺序,若是没有,表明列表已经有序
swapped = false;
for (let i = 0; i < originValues.length - 1 - hasOrderedCnt; i++) {
// 若是前一元素大于后一元素,交换顺序
if (originValues[i] > originValues[i + 1]) {
const tmp = originValues[i];
originValues[i] = originValues[i + 1];
originValues[i + 1] = tmp;
swapped = true;
}
}
// 每一轮以后,都有一个元素排好顺序
hasOrderedCnt++;
} while (swapped);
return originValues;
};
复制代码
用了一个变量hasOrderedCnt
来记录已经排序的个数,这样内循环就不要去比较已经排序的元素了。
时间复杂度
通过几轮修改,数组正序时,最优复杂度能够达到O(n);逆序时,最差复杂度O(n2)。
稳定性
算法中,每次只有前一个元素大于后一个元素,才会进行交换。因此数值相同的两个元素,不会发生位置互换,因此能够保持以前先后顺序。故,冒泡排序是稳定的排序。
本章节介绍了几种冒泡排序的实现。算法思想仍是算简单的,但也是效率比较慢的。虽然比较简单,但仍是有不少变种,例如左右冒泡、从大到小的排序、数组元素不是数值等等,都须要本身动手去写才能理解透。
[1] 动画演示
[2] tutorials point 教程
[3] The Bubble sort algorithm
[4] Sorting Algorithms