JavaScript冒泡排序优化之路

309 阅读2分钟

平均时间复杂度 O(n²)
最差时间复杂度O(n²)
最优时间复杂度(指的是优化完以后) O(n)

1. 冒泡排序作为最基础的排序算法之一,想必大家都不陌生,简单粗暴;

function bubbling(list){
      for(let i = 0; i < list.length; i++){
        for(let j = 0; j < list.length; j++){
          if(list[j] > list[j+1]){ //从小到大排序
            const temp = list[j];
            list[j] = list[j+1];
            list[j+1] = temp;
          }
        }
      }
      return list;
    }

2、有的时候会发现当原始数组是即将排序好的数组时,例如[1,2,3,5,4], 其实走一遍循环就已经完成了排序,后续的循环都是白费力气不讨好,所有我们要对无用的外层循环做个拦截;

下面这种优化后 复杂度为O(n)

    function bubbling(list){
      for(let i = 0; i < list.length; i++){
        let flag = false; //标志位
        for(let j = 0; j < list.length; j++){
          if(list[j] > list[j + 1]){ //从小到大排序
            const temp = list[j];
            list[j] = list[j + 1];
            list[j + 1] = temp;
            flag = true;
          }
        }
        if(!flag) break;
      }
      return list;
    }
    
    const exampleList = [1,2,3,5,4];
    bubbling(exampleList)

优化前

image.png

优化后

image.png

3、最优版,减少内循环的次数;

    function bubbling(list){
      let len = list.length - 1;
      for(let i = 0; i < list.length - 1; i++){
        let flag = false; //标志位
        let lastIndex = 0;
        for(let j = 0; j < len; j++){
          if(list[j] > list[j + 1]){ //从小到大排序
            const temp = list[j];
            list[j] = list[j + 1];
            list[j + 1] = temp;
            flag = true;
            lastIndex = j;
          }
          console.log(j);
        }
        len = lastIndex;
        if(!flag) break;
        console.log(`第${i}次`, list);
      }
      return list;
    }

优化前
image.png

优化后
image.png

细心的同学会发现,第0次以后,最后一个位置不再变化,第1次后,最后两个位置不再变化,第2次以后,最后三个位置不再变化,因此在内循环的时候,就没必要比较最后的几个元素;

我们把每次内循环时,发生交换的最后元素位置记录下来,下次内循环时,执行到该位置,就可以中断;

通过上面两个图片可以清晰的看出内循环少循环了很多次,从而达到我们优化的效果;

两次优化,第一次是优化最外层的循环,第二次是优化内循环;