优美编程

解剖前端面试之冒泡排序

2020-04-10  本文已影响0人  小遁哥

冒泡排序没什么实战价值,多用于练习算法,往大了说是逻辑思维,因为它算不上一个好算法。
让我想起了很多往事,学习它就像饭后抽根烟的感觉是一样的,吃饭是重要的,抽烟只是为了爽。

总上所述,文笔会有些放肆,请谨慎观看

好的文章总是要有金句

容易忘记的东西,需要在不同的阶段重新拾起,往往你会得到新的理解,因为总是会剩下一些你忘不掉

这次之所以很兴奋,很放肆,是因为这是第一次被要求手写一下冒泡排序,并且我再一次的忘了,以前在不同的阶段我都有接触过,可能是面试前的准备,也可能是突然想起来看看自己能不能写出来,从看一下文章大致记一下,到尝试理解冒泡两个字。

背景介绍

电话面试后去公司,要求手写,开始的时候我是拒绝的(之前还婉拒了HR说的一小时答的笔试题),面试官提到想看看在这种场景下,10分钟能不能写出来。

我十分流利的写下了下面的解答,三十秒都不到吧,我内心毫无波澜。

正文的味道

我写的冒泡排序

    let arr = [1, 2, 34, 2, 19, 10];
    for (let i = 0; i < arr.length; i++) {
      for (let j = i; j < arr.length; j++) {
        if (arr[i] > arr[j]) {
          [arr[i], arr[j]] = [arr[j], arr[i]];
        }
      }
    }
    console.log(arr);

简单的看一眼,想起冒泡的时间复杂度为
n^2

就把let j = i; 改成 j = 0,虽然不影响结果...

回到家后,在知乎上看了些关于<<士兵突击>>的帖子,突然想在浏览器上运行一下,反正也没几行代码,令我惊奇的是结果竟然对了。

不改还好点,像个选择排序,即便如此仍然差了很多细节

我的记性不好,有些事情告诉自己记住简单的就行了,这是个隐患,没有细节就意味着粗糙

来个粗糙点的冒牌排序

    let arr = [1, 2, 34, 2, 19, 10];
    for (let i = 0; i < arr.length - 1; i++) {
      for (let j = 0; j < arr.length - 1; j++) {
        if (arr[j] > arr[j + 1]) {
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
      }
    }
    console.log(arr);

我想这个arr.length - 1不用记在写的时候也能想起来,因为[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];这行代码中是jj+1在交换,
首先二层不处理会报错
其次最后剩一个数也没必要比较,所以一层要处理

更深的一层在于总能把最大的那个带到两边,配上if (arr[j] > arr[j + 1]) {就是最右边

第一次就把最大的带到最右边,那么下一次最后一个不用参与比较

细节的冒泡排序

    let arr = [1, 2, 34, 2, 19, 10];
    for (let i = 0; i < arr.length - 1; i++) {
      for (let j = 0; j < arr.length - i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
      }
    }
    console.log(arr);

没检查出这层在于我没有用例子带入,将算法实际的在脑中过一遍这确实是应该加强的地方,整好了我的效率绝对能再上一层

优化的冒泡排序

优化不能偏离算法的本质
如果二层循环始终没有进入if (arr[j] > arr[j + 1]) {,说明前面的已经是有序序列

    let arr = [1, 2, 34, 2, 19, 10];
    for (let i = 0; i < arr.length - 1; i++) {
      let isSwap = false;
      for (let j = 0; j < arr.length - i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
          isSwap = true;
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
      }
      if (!isSwap) {
        break;
      }
    }
    console.log(arr);

下次我忘记的时候能写出这个就很满足了

因为我现在再碰到这种文章已经不会点进去了,自己突然想搞也不可能,只有可能是被面试的时候,被要求手写的概率也很小,且行且珍惜吧!

再细一点的优化

记录发生交换的最后位置,意味着之后的已经不用交换了,适用于后面本就有序数的情况

    let arr = [1, 4, 3, 2, 5, 6, 7];
    let lastSwapIndex = arr.length;
    let pos = 0;
    for (let i = 0; i < arr.length - 1; i++) {
      let isSwap = false;
      for (let j = 0; j < lastSwapIndex; j++) {
        if (arr[j] > arr[j + 1]) {
          isSwap = true;
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
          pos = j;
        }
      }
      if (!isSwap) {
        break;
      }
      // if (arr.length - i - 1 > pos) {
      //   debugger;
      // }
      lastSwapIndex = pos;
    }
    console.log(arr);

let arr = [1, 4, 3, 2, 5, 6, 7];对比let arr = [1, 2, 34, 2, 19, 10];这种用例是比较提倡的,查看结果时直观

[1, 4, 3, 2, 5, 6, 7]
第一次 [1, 3, 2, 4, 5, 6, 7]
按照上一个方案,只是7不用比,新方案的话,4不用比了
我是开发者工具弄出来的,更有趣的玩法是,写出代码后,还要举出能起到作用的测试用例并且说出具体流程...

刺激!还是先写出来吧,我有点怂。

结语

其实还有很多方式能够印象深刻,似乎对于现在的我来说,都不太现实。
我这个人吧,确实不太适合面试,如果能经常锻炼,我肯定是能够进步的,还是提不起兴趣吧。
值得庆幸的是,今天肯定不用担心失眠,明天就能早起去吃油条了,奥利给!


上一篇 下一篇

猜你喜欢

热点阅读