背景😯

今天重刷 leetcode 的时候,刷到这么一道题102. 二叉树的层序遍历,很简单的二叉树遍历题目,三下五除二把代码写了出来

其他代码都不重要,重要的是第 27 行那里。

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val, left, right) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.left = (left===undefined ? null : left)
  6. * this.right = (right===undefined ? null : right)
  7. * }
  8. */
  9. /**
  10. * @param {TreeNode} root
  11. * @return {number[][]}
  12. */
  13. var levelOrder = function(root) {
  14. if(!root) return[]
  15. const res = [],
  16. que = [root, null]
  17. let tmpLevel = []
  18. while(que.length){
  19. const t = que.shift()
  20. if(t){
  21. tmpLevel.push(t.val)
  22. t.left && que.push(t.left)
  23. t.right && que.push(t.right)
  24. }else {// t为null
  25. res.push(tmpLevel)
  26. // tmpLevel.length = 0
  27. tmpLevel = [] //第27行
  28. que.length && que.push(null)
  29. }
  30. }
  31. return res
  32. };

const 和 let 的书写习惯

其他代码都不重要,重要的是第 27 行那里。我个人写代码的习惯时,能用const就用const,用了const后面要修改时报错了再改为let

不知道你们是怎么呢

所以自然我的tmpLevel一开始就是用const定义,也就自然而然地在tmpLevel = []处报错了。
改成let定义后,也是自然而然地过了,但是我一下想起来,好像用tmp.length = 0就能清空数组呀,还不用改const~ 说不定性能还高一点? 真机(han)智(han) [doge]

数组清空的方式😏

ok 我们先小小复习一下数组清空的方式

splice

首先是 JavaScript 数组自身API:splice
arr.splice 方法可以说是处理数组的瑞士军刀。它可以做所有事情:添加,删除和插入元素。

语法:arr.splice(start[, deleteCount, elem1,..., elemN]) 它从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, …, elemN。最后返回已被删除元素的数组。

那直接从下标为 0,删掉数组长度那么长不就好了~

  1. let arr = [1,2,3]
  2. arr.splice(0,arr.length);
  3. console.log(arr)//[]

arr.length

如果我们手动增加它,则不会发生任何有趣的事儿。但是如果我们减少它,数组就会被截断。该过程是不可逆的

  1. let arr = [1, 2, 3, 4, 5];
  2. arr.length = 2; // 截断到只剩 2 个元素
  3. console.log(arr); // [1, 2]
  4. arr.length = 5; // 又把 length 加回来
  5. console.log(arr[3]); // undefined:被截断的那些数值并没有回来

也就是说,我们直接将其赋值为0 就行

  1. let arr = [1, 2, 3, 4, 5];
  2. arr.length = 0
  3. console.log(arr); // []

= []

直接将 arr 重新赋值为空数组,之前的数组如果没有被引用就等待垃圾回收就完事了~

三种方式的性能比较😎

我当时就想啊,算法题,肯定要追求一下效率的吗,上性能最好的干他~

测试

  1. let a = [];
  2. console.time('splice');
  3. for(let i =0 ; i < 10;i++){
  4. a = new Array(100).fill(111)
  5. a.splice(0,a.length);
  6. }
  7. console.timeEnd('splice');
  8. console.time('length');
  9. for(let i =0 ; i < 10;i++){
  10. a = new Array(100).fill(111)
  11. a.length = 0;
  12. }
  13. console.timeEnd('length');
  14. console.time('赋值[]');
  15. for(let i =0 ; i < 10;i++){
  16. a = new Array(100).fill(111)
  17. a = [];
  18. }
  19. console.timeEnd('赋值[]')

一开始用了贼大的循环,直接卡死了🙃

结果

image.png
ok 那就是直接赋值性能最好, 直接修改 length 为0 性能其次,但是相差不多呀,用lenngth还能保持我使用const的习惯,那就上 第二种方法呗

结果😐

image.png
结果一看,不对劲啊,这清零怎么把最后结果的也给清了,猛地一下想到原因,直呼自己睿智

原因

其实这个我是知道的,但是写题的时候真就没想起来

注意 JS 数组重置的方式,虽然经测试arr.length = 0的速度会比arr = []快很多,但是这样是得不到正确答案的,原因是因为:
arr =[]创建的是一个新的数组,并为其赋予一个新的引用。其他引用不收影响,仍指向原数组
arr.length = 0修改数组本身,就算通过不同的变量访问它,得到的是同一个数组


直观测试

我们做个测试直观一点
image.png

测试图解

image.png
也就是说 =[] 是创建了一个新数组,length=0之后操作的仍是原来的引用~所以答案数组中的自然也就给一同清零了

总结🤔

唉这个东西,真是基础不牢地洞山摇啊~虽然是非常基础的知识点,但是可能到实际运用中就可能带来一些没料到的 bug 。
一开始测到性能差异,先入为主的就以为因为=[]会创建一个新的引用吗~就会导致性能较差,导致性能测试结果都看歪来了😖也就导致自己”美滋滋地”用上不用改const的方法…

不过有时这也说明,该用 let时就用let啊,不要头铁…
基础、基础,还是 的基础!