如下是一个基本的冒泡排序算法执行过程

  • 外部循环len
  • 内部循环每次用arr[i]的值与arr[j]的值进行比较
  • 由于外部循环的i变量每次进入内部循环都不会改变,也就是arr[i]的值进入内部循环后,都会以自身与arr[j](也就是整个数组的所有元素)比较一轮,结果是小于arr[i]的数值都会被放到数组的开头
  • 经过外层循环的一次次的进入内层循环的比对后,数组也依照从小到大的顺序排列了
let arr = [1, 3, 2]
const len = arr.length

for (let i = 0; i < len; i++) {
  for (let j = 0; j < len; j++) {
    const prevNum = arr[i]
    const nextNum = arr[j]
    
    // 如果成立,交换值的位置
    if (prevNum > nextNum) {
      arr[i] = nextNum
      arr[j] = prevNum
    }
  }
}

步骤分解

第一轮外部循环 i = 0

i = 0;
j = 0;
arr = [1, 3, 2];

arr[i]也就是prevNum的值为1
arr[j]也就是nextNum的值为1

prevNum > nextNum不成立
交换条件不满足
arr保持现状[1, 3, 2]
i = 0;
j = 1;
arr = [1, 3, 2];

arr[i]也就是prevNum的值为1
arr[j]也就是nextNum的值为3

prevNum > nextNum不成立
交换条件不满足
arr保持现状[1, 3, 2]
i = 0;
j = 2;
arr = [1, 3, 2];

arr[i]也就是prevNum的值为1
arr[j]也就是nextNum的值为2

prevNum > nextNum不成立
交换条件不满足
arr保持现状[1, 3, 2]
i = 0;
j = 3;
arr = [1, 3, 2];

j < len不成立
循环结束

第二轮外部循环 i = 1

i = 1;
j = 0;
arr = [1, 3, 2];

arr[i]也就是prevNum的值为3
arr[j]也就是nextNum的值为1

prevNum > nextNum成立
交换位置条件满足
arr更新为[3, 1, 2]
i = 1;
j = 1;
arr = [3, 1, 2];

arr[i]也就是prevNum的值为1
arr[j]也就是nextNum的值为1

prevNum > nextNum不成立
交换条件不满足
arr保持原样[3, 1, 2]
i = 1;
j = 2;
arr = [3, 1, 2];

arr[i]也就是prevNum的值为1
arr[j]也就是nextNum的值为2

prevNum > nextNum不成立
交换条件不满足
arr保持原样[3, 1, 2]
i = 1;
j = 3;
arr = [3, 1, 2];

j < len不成立
循环结束

第三轮外部循环 i = 2

i = 2;
j = 0;
arr = [3, 1, 2];

arr[i]也就是prevNum的值为2
arr[j]也就是nextNum的值为3

prevNum > nextNum不成立
交换条件不满足
arr保持原样[3, 1, 2]
i = 2;
j = 1;
arr = [3, 1, 2];

arr[i]也就是prevNum的值为2
arr[j]也就是nextNum的值为1

prevNum > nextNum成立
交换条件满足
arr更新为[3, 2, 1]
i = 2;
j = 2;
arr = [3, 2, 1];

arr[i]也就是prevNum的值为2
arr[j]也就是nextNum的值为2

prevNum > nextNum不成立
交换条件不满足
arr保持原样[3, 2, 1]
i = 2;
j = 3;
arr = [3, 2, 1];

j < len不成立
循环结束

第四轮外层循环 i = 3

i < len不成立
循环结束

现在数组最终为[3, 2, 1]

优化1

分析

每次内部循环都会从let j = 0开始,但其实这一步是没必要的,因为按照我们的算法,第一次进入内部循环时,ij都是0arr[i]arr[j]取的都是同一个值,而当非第一次的时候,索引为0的取值就是上次最大的值。两种都不会发生位置交换。所以可以忽略。

如何忽略呢?
答案就是ii的值其实隐含了已经遍历对比过的索引位置,我们只需要让内部循环从i的索引开始。

for (let i = 0; i < len; i++) {
  for (let j = i; j < len; j++) {
  }
}

验证

用一个大数组,记录下循环次数
image.png

下面看下优化过的
image.png

效果显著。

优化2

分析

由于外部循环的i和内部循环的j初始化会相等,所以arr[i]arr[j]会取到同一个位置的值比较一次,相同的值对比是不会发生位置交换的,也就没有必要对比,既然我们知道了,那么怎么优化这个操作呢?

答案就是让内部j从第二位开始,避免和i取同一个值的情况。

for (let i = 0; i < len; i++) {
  for (let j = i + 1; j < len; j++) {
  }
}

验证

image.png
聊胜于无。哈哈。


热饭班长
3.7k 声望434 粉丝

先去做,做出一坨狗屎,再改进。