小白的冒泡排序及优化方案| 8月更文挑战

349 阅读4分钟

前言

冒泡排序是我接触的第一种算法,最开始只是明白个大概,直到自己手写的时候才明白里面的思想,写下这篇文章,也是方便自己日后回顾。


介绍

用百度的话说:

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

用自己的话说:

冒泡排序是一种比较简单的排序算法,对数组进行升序排序时,会比较相邻元素,如果第一个元素大于第二个元素,就交换位置,否则就比较下两个元素。

[4,2,3,1]
使用冒泡排序,第一遍比较42,4>2,交换位置,得到
[2,4,3,1]
重复上一步,比较434>3,交换位置,得到
[2,3,4,1]
比较41,4>1,交换位置,得到
[2,3,1,4]
此时找到了数组中最大的元素4,之后重复上面的步骤去比较23...这里就不多做赘述。
在第一趟排序过程中,4索引的变化:

  index:3
     ↑
  index:2
     ↑
  index:1
     ↑
  index:0

像是一个气泡不断上升且变大,故名冒泡排序

代码实现

最简版

function bubble(arr) {
    let len = arr.length
    for(let i = 0 ; i < len; i ++) {
        // 当arr[j]为最后一个元素时,arr[j+1]不存在,无法比较,所以这里是j < len - 1
        for(let j = 0; j < len - 1; j ++) {
            console.log(`第一个元素:${arr[j]}   第二个元素:${arr[j+1]}`)
            if(arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            }
        }
        console.log(`=============================第${i+1}趟排序,结果为${arr}`)
    }
    return arr
}

console.log(bubble([4,2,3,1]))

// 第一个元素:4   第二个元素:2
// 第一个元素:4   第二个元素:3
// 第一个元素:4   第二个元素:1
// =============================第1趟排序,结果为2,3,1,4
// 第一个元素:2   第二个元素:3
// 第一个元素:3   第二个元素:1
// 第一个元素:3   第二个元素:4
// =============================第2趟排序,结果为2,1,3,4
// 第一个元素:2   第二个元素:1
// 第一个元素:2   第二个元素:3
// 第一个元素:3   第二个元素:4
// =============================第3趟排序,结果为1,2,3,4
// 第一个元素:1   第二个元素:2
// 第一个元素:2   第二个元素:3
// 第一个元素:3   第二个元素:4
// =============================第4趟排序,结果为1,2,3,4
// [ 1, 2, 3, 4 ]

从输出结果来看,每趟遍历都要比较3次,但根据上面的冒泡原理可知,每遍历一次数组,就会找出最大/第二大/第三大...的数,所以上面的输出结果中,第2趟排序的3和4无需比较,因为第1趟排序已经可以确认4是最大的元素了。根据这个思路,优化方案

优化版v1

function bubble(arr) {
    let len = arr.length
    for(let i = 0 ; i < len; i ++) {
        // 注意这里,因为每遍历一次数组就可以找到最大的项,也就是说每遍历一次需要比较的元素就少一个
        for(let j = 0; j < len - 1 - i; j ++) {
            console.log(`第一个元素:${arr[j]}   第二个元素:${arr[j+1]}`)
            if(arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            }
        }
        console.log(`=============================第${i + 1}趟排序结束,结果为${arr}`)
    }
    return arr
}

console.log(bubble([4,2,3,1]))

// 第一个元素:4   第二个元素:2
// 第一个元素:4   第二个元素:3
// 第一个元素:4   第二个元素:1
// =============================第1趟排序结束,结果为2,3,1,4
// 第一个元素:2   第二个元素:3
// 第一个元素:3   第二个元素:1
// =============================第2趟排序结束,结果为2,1,3,4
// 第一个元素:2   第二个元素:1
// =============================第3趟排序结束,结果为1,2,3,4
// =============================第4趟排序结束,结果为1,2,3,4
// [ 1, 2, 3, 4 ]

从上面代码可以看出,比较次数从原来的4趟12次变成了4趟6次,但还是能发现,当第三躺排序结束的时候,其实已经完成排序了,但还是进行了第4趟排序。我们想要的是如果后面的元素都不需要比较(已经是排序好的),则直接结束排序,根据这个思路,优化方案

优化版v2

function bubble(arr) {
    let len = arr.length
    for (let i = 0; i < len; i++) {
        // 定义一个flag,代表后续元素是否需要交换位置
        let flag = true
        for (let j = 0; j < len - 1 - i; j++) {
            console.log(`第一个元素:${arr[j]}   第二个元素:${arr[j + 1]}`)
            if (arr[j] > arr[j + 1]) {
                // 需要交换位置,flag改为false
                flag = false;
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            }
        }
        // 如果`某次循环`中不需要交换位置,那么意味着排序已经完成,无需继续遍历
        if (flag) break;
        console.log(`=============================第${i + 1}趟排序结束,结果为${arr}`)
    }
    return arr
}

console.log(bubble([4, 2, 3, 1]))

// 第一个元素:4   第二个元素:2
// 第一个元素:4   第二个元素:3
// 第一个元素:4   第二个元素:1
// =============================第1趟排序结束,结果为2,3,1,4
// 第一个元素:2   第二个元素:3
// 第一个元素:3   第二个元素:1
// =============================第2趟排序结束,结果为2,1,3,4
// 第一个元素:2   第二个元素:1
// =============================第3趟排序结束,结果为1,2,3,4
// [ 1, 2, 3, 4 ]

总结

冒泡排序的性能并不高,正常工作中也不常用,但是作为一种简单的排序方式,新手也能很容易理解。