我是如何看懂冒泡排序的(思路拆解)

204 阅读3分钟

前置知识

学会冒泡排序首先需要学会=> 数组的基本用法 丶for循环丶两个变量交换值
假设你已经会这几个知识点

for循环for循环
    for (let i = 0; i < 10; i++) {
      console.log(i)
    }
两个变量交换值 两个变量交换值
    let a = 1
    let b = 2
    let temp = a
    a = b
    b = temp
    console.log(a, b) //2 1

第一步,首先看一个示例

定义一个数组,结合前面两个知识点
用一个for循环,从第一个数开始,前一个数和后一个比较, 小的话不变,大的话交换值.
最终让其中一个最大的数到最右边

    let arr = [2, 6, 5, 1, 3]
    for (let i = 0; i < arr.length; i++) {
      if (arr[i] > arr[i + 1]) {
        let temp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = temp
      }
    }
    document.write(arr)
    //此时打印出来的结果是 2,5,1,3,6  最大的6到了最后面

↓解析循环过程↓

  • 第一个for循环: i = 0, arr[i] = 2 , arr[i + 1] = 6, 2比6小, 不变
  • 第二个for循环: i = 1, arr[i] = 6 , arr[i + 1] = 5, 6比5大, 6和5交换位置. 此时的数组为 [2, 5, 6, 1, 3]
  • 第三个for循环: i = 2, arr[i] = 6 , arr[i + 1] = 1, 6比1大, 6和1交换位置. 此时的数组为 [2, 5, 1, 6, 3]
  • 第四个for循环: i = 3, arr[i] = 6 , arr[i + 1] = 1, 6比1大, 6和1交换位置. 此时的数组为 [2, 5, 1, 3, 6]
  • 第五个for循环: i = 4, arr[i] = 6 , arr[i + 1] = undefined, 没有必要的对比

以上分析可得知, 循环的次数比数组长度少一次,数组长度5,只需要循环4次
i < arr.length 可以优化成 i < arr.length - 1
另外既然一次循环能让一个较大的数往右移,那么多次循环是不是就可以达到效果呢
于是我猜测只要4轮就能移动完毕, 于是复制了4份一样的循环查看效果

第二步,根据分析循环四遍

    for (let i = 0; i < arr.length - 1; i++) {
      if (arr[i] > arr[i + 1]) {
        let temp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = temp
      }
    }
    for (let i = 0; i < arr.length - 1; i++) {
      if (arr[i] > arr[i + 1]) {
        let temp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = temp
      }
    }
    for (let i = 0; i < arr.length - 1; i++) {
      if (arr[i] > arr[i + 1]) {
        let temp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = temp
      }
    }
    for (let i = 0; i < arr.length - 1; i++) {
      if (arr[i] > arr[i + 1]) {
        let temp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = temp
      }
    }
    document.write(arr) //1,2,3,5,6

结果果然是我想要的 1,2,3,5,6
当我们遇到重复执行多遍的代码时应该怎么办呢? 那必然是循环啦
于是我在外面又套个外层循环,循环4次, 4次怎么来的呢? 刚好又是数组长度减一,代码如下↓

    for (let j = 0; j < arr.length - 1; j++) {
      for (let i = 0; i < arr.length - 1; i++) {
        if (arr[i] > arr[i + 1]) {
          let temp = arr[i]
          arr[i] = arr[i + 1]
          arr[i + 1] = temp
        }
      }
    }
    // 此时打印出来的结果还是 1,2,3,5,6
    document.write(arr)

第三步,优化性能

到这里看似已经OK,但仔细想想发现一个问题
内层循环每次都会从第一个数开始,两两对比,直到最后一个数
但除了第一轮以外,实际上后面每一轮需要对比的次数可以减1
比如只执行一次外层循环,就让最大的数值到达最后一个位置
接着第二个外层循环已经不需要再对最后一个数值进行对比, 因为它肯定是最大了
由此可知内层循环的循环次数是依次递减的
第一次对比4轮
第二次对比3轮
第三次对比2轮
第四次对比1轮
用i < arr.length - 1 就不适合了, 会造成严重的性能浪费
如何递减呢? 有个办法 改成 i < arr.length - j - 1
因为j是从0递增的,数组长度固定 减去j后会让循环判断条件越来越小,由此达到递减的效果
第一次对比4轮 5 - 0 - 1
第二次对比3轮 5 - 1 - 1
第三次对比2轮 5 - 2 - 1
第四次对比1轮 5 - 3 - 1
完美解决! 最终代码如下

    for (let j = 0; j < arr.length - 1; j++) {
      for (let i = 0; i < arr.length - j - 1; i++) {
        if (arr[i] > arr[i + 1]) {
          let temp = arr[i]
          arr[i] = arr[i + 1]
          arr[i + 1] = temp
        }
      }
    }
    // 此时打印出来的结果还是 1,2,3,5,6  并且性能没有浪费
    document.write(arr)