图文结合:快速理解冒泡排序

915 阅读4分钟

基本思想

冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误(例如,从大到小)就把它们交换过来。

分析

假如,我们要将 12、30、66、40 这四个数进行从大到小的排序。那么稍想一下,大家就能立刻明白,数字越小越靠后。

首先比较第 1 位和第 2 位的大小,它们分别是 12 和 30。发现 12 比 30 要小,因此要交换这两个数的位置。交换之后的顺序是:30、12、66、40。

截屏2021-10-21 12.04.56.png

接着比较第 2 位和第 3 位的大小,它们分别是 12 和 66。发现 12 比 66 要小,因此要交换这两个数的位置。交换之后的顺序是:30、66、12、40。

截屏2021-10-21 12.07.14.png

接着再比较第 3 位和第 4 位的大小,它们分别是 12 和 40。发现 12 比 40 要小,因此要交换这两个数的位置。交换之后的顺序是:30、66、40、12。

截屏2021-10-21 12.09.57.png

经过 3 次比较后,我们发现四个数中最小的一个数已被归位(就是排在了最后一位)。而每将一个数归位,我们将其称为“一趟”。

冒泡排序的原理是:每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,以此类推,直到所有的数归位。而现在我们的排序只是将四个数中最小的一个归位了。所以,还要继续排,直到将所有数归位即按从大到小排好。

现在,我们开始第二趟排序,目的是将第二小的数归位。按照“第一趟”的比较方式,我们需要依次比较第 1 位和第 2 位,第 2 位和第 3 位。值得注意的是,此时已不需要再比较第 3 位和第 4 位。因为在第一趟结束后,我们就确定了第 4 位上放的是最小的数了。因此第二趟排序结果是:66、40、30、12。

接着是第三趟,还是一样的比较方式,不过此时我们只需要比较第 1 位和第 2 位,因为第 3 位和第 4 位是已经归位的数,它们无需再进行比较。由于第 1 位比第 2 位大,所以不用交换位置,因此最终排序结果是:66、40、30、12。

分析完冒泡排序的思路,现在我们总结一下:

  1. 如果有 n 个数进行排序,那么将有 n-1 个数归位,也就是说要进行 n-1 趟排序操作。
  2. “每一趟”排序都要从第 1 位开始进行相邻两个数的比较,且将较小的数放在后面(前面),比较完毕后向后挪一位并继续比较下面两个相邻数的大小。
  3. 重步上述步骤,直到最后一个尚未归位的数,已归位的数无需再比较。

代码实现

针对本次案例,下面写出了冒泡排序的三种实现方式以供同学们参考学习。

const list = [12, 30, 66, 40];
const list_copy = list.slice(); // 获取 list 数组的一个副本(目的是不改变原数组)

// 冒泡排序执行
const newArr = bubbleSort(list_copy);
const newArr2 = bubbleSort2(list_copy);
const newArr3 = bubbleSort3(list_copy);
console.log("冒泡排序——执行完成:", newArr, newArr2, newArr3);

// 冒泡排序一
function bubbleSort(arr) {
  const len = arr.length;
  let i, j, temp;

  // len 个数排序,只需进行 len-1 趟
  for (i = 0; i < len - 1; i++) {
    // 从第 1 位开始比较直到最后一个尚未归位的数,已经归位的数无需再进行比较(这是 j < len-i 的原因)。
    for (j = 0; j < len - i; j++) {
      // 比较大小并交换
      if (arr[j] < arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
  return arr;
}

// 冒泡排序二
function bubbleSort2(arr) {
  const len = arr.length - 1;
  let i, j, swap, cLen = arr.length - 1;

  for (i = 0; i < len; i++) {
    for (j = 0; j < cLen; j++) {
      // 比较大小并交换
      if (arr[j] < arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
    // 内层for循环的比较次数随着外层遍历的进行依次减少,与上面的 len - 1 起到同样的作用
    cLen--;
  }
  return arr;
}

// 冒泡排序三
function bubbleSort3(arr) {
  let j, temp, len = arr.length - 1;

  for (j = 0; j < len; j++) {
    if (arr[j] < arr[j + 1]) {
      temp = arr[j];
      arr[j] = arr[j + 1];
      arr[j + 1] = temp;
    }
    // 当 j 在循环中大到最大值时,就重置,同时递减 len
    if (j === len - 1) {
      j = -1; // 将 j 赋值为 -1,经过 j++ 重置为 0
      len--; // 每一趟排序后,减少循环次数,与上面的 len - 1 起到同样的作用
    }

  }
  return arr;
}

最后

学习犹如逆水行舟,不进则退。老铁,既然来了,点个赞👍,再走呗!