冒泡排序是一种非常容易理解的排序方式。比如以下的待排序数组:
var seq = [9, 2, 6, 4, 3, 1, 7, 5, 6];
通过对数组进行遍历,每一趟遍历都依次从左至右两个两个的元素进行比较,若二者次序不正确,就交替二者的位置,描述起来略显枯燥,请看以下图例:
上图由左至右,为一趟排序,观察9
这个数字其实已经排序到最终的位置。而且整个过程是不是像9
这个数字从底部上升到顶部?其实次大的7
也会在第二趟排序中从他现有的位置上升到顶部第二的位置。整体就像一个个气泡从水底逐渐冒泡到顶部的过程,故名冒泡排序。
可以看到冒泡排序的核心就是每一趟遍历若前者大于后者,则交换二者的位置。当遍历完成即完成了排序的过程,下面我用javascript语言来实现:
function bubbleSort(seq) {
let i, j, temp;
// 这里之所以倒着遍历,最因为每一趟遍历都确定了最后一个元素。
// 倒着遍历恰好能够子遍历中的截止元素索引
for (i=seq.length; i > 0; i--) {
for (j=1;j<i;j++) {
if (seq[j] < seq[j-1]) {
// 若次序不对就交换位置
temp = seq[j];
seq[j] = seq[j-1];
seq[j-1] = temp;
}
}
}
}
bubbleSort(seq)
console.log(seq) // [1, 2, 3, 4, 5, 6, 6, 7, 9 ]
怎么样,够简单吧。但是呢,还有一个可优化的点。即若序列本身已经是有序的,那么冒泡排序一趟下来实际上并未发生任何交换。而且之后的每一趟都不会发生交换。
即,若在某一趟排序完成之后,若发现本趟排序并未移动任何元素。那么可以认定当前的序列已经有序。
接下来稍微改动一下代码如下:
function bubbleSort(seq) {
let i, j, temp, flag = 1; // 添加一个标志位flag
// 这里之所以倒着遍历,最因为每一趟遍历都确定了最后一个元素。
// 倒着遍历恰好能够子遍历中的截止元素索引
for (i=seq.length; i > 0 && flag; i--) {
flag = 0; // 假设已经有序,如果后续发现不是有序,会改变这个标志位。
for (j=1;j<i;j++) {
if (seq[j] < seq[j-1]) {
// 若次序不对就交换位置
temp = seq[j];
seq[j] = seq[j-1];
seq[j-1] = temp;
flag = 1; // 本趟遍历存在位置交换,还需继续遍历
}
}
console.log('遍历了一次'); //测试若已经有序的序列,仅仅执行一次遍历
}
}
const res = [1,2,3,4,5,6,7]
bubbleSort(res) // 遍历了一次
console.log(res); // [1, 2, 3, 4, 5, 6, 6, 7, 9 ]
如上,若队列已经有序,仅仅会执行一次遍历。所以冒泡排序也适合那种本身已经部分有序的情况。