• 作者:老汪软件技巧
  • 发表时间:2024-09-05 21:02
  • 浏览量:

每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。

1. 一个类型体操

类型体操题目集合String to Union

实现一个将接收到的 String 参数转换为一个字母 Union 的类型。

例如:

type Test = "123";
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"

分析

这无非就是将元组的遍历变成对字符串的遍历而已;而对于字符串的遍历,我们使用的是 模板字符串。遍历每一个字符,让其与剩余的部分 union 起来。但是这里有个问题就是对于 "" 如何处理?

尝试写出

type StringToUnionextends string> = T extends `${infer F}${infer R}`
  ? F | StringToUnion
  : T;

测试用例

type Result = StringToUnion<Test>; // "" | "1" | "2" | "3"

这是不对的哈,没有前面的 "" 才是对的。正确的做法是将 : T 换成 : never T 不满足条件的时候就是 "" 这个时候不需要拼上去。

参考答案

type StringToUnionextends string> = T extends `${infer L}${infer R}`
  ? L | StringToUnion
  : never;

经验总结

一定要想清楚递归出口处的值。

2. 两个 Leetcode 题目

刷题的顺序参考这篇文章 LeeCode 刷题顺序

2.1 [48] 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
提示:
n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000

尝试实现:

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var rotate = function (matrix) {
  const r = matrix.length;
  if (r === 1) return matrix;
  const c = matrix[0].length;
  const count = Math.ceil(r / 2);
  for (i = 0; i < count; i++) {
    for (j = i; j < c - i - 1; j++) {
      const tmp = matrix[r - j - 1][i];
      matrix[r - j - 1][i] = matrix[r - i - 1][c - j - 1];
      matrix[r - i - 1][c - j - 1] = matrix[j][c - i - 1];
      matrix[j][c - i - 1] = matrix[i][j];
      matrix[i][j] = tmp;
    }
  }
  return matrix;
};

我的思路:

得分结果: 32.49% 58.71%

总结提升:

还是很容易混淆个数和序列下标的区别。在除 2 之后的取整上想不清楚。向左旋转还是向右旋转的问题。2.2 [73] 矩阵置零

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
示例 1:
输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 2:
输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
提示:
m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1
进阶:
一个直观的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个仅使用常量空间的解决方案吗?

尝试完成:

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var setZeroes = function (matrix) {
  const r = matrix.length;
  const c = matrix[0].length;
  for (let i = 0; i < r; i++) {
    for (let j = 0; j < c; j++) {
      const isZero = Object.is(matrix[i][j], +0);
      if (isZero) {
        // 清除同行
        for (let _j = 0; _j < c; _j++) {
          if (!Object.is(matrix[i][_j], +0)) matrix[i][_j] = -0;
        }
        // 清除同列
        for (let _i = 0; _i < r; _i++) {
          if (!Object.is(matrix[_i][j], +0)) matrix[_i][j] = -0;
        }
        matrix[i][j] = +0;
      }
    }
  }
  return matrix;
};

我的思路:

利用 js 特有的 Object.is 可以区别正 0 和 负 0,将重置之后的 0 视为 -0,这样就不怕相互覆盖了。

得分结果: 99.56% 99.51%

总结提升:

-0 +0 是真的好用!3. 三个前端题目箭头函数的 this 指向

下面使用 babel 转换箭头函数之后的结果说明这个问题:

// 源码
const obj = {
  getArrow() {
    return () => {
      console.log(this);
    };
  },
};
// 经过babel转译之后的结果
var obj = {
  getArrow: function getArrow() {
    var _this = this;
    return function () {
      console.log(_this);
    };
  },
};

手写一个函数,实现数组扁平化的功能所谓数组扁平化指的就是如果数组中的元素依然是数组,则将内嵌数组中的元素拿出来直接放到上层数组中即可。2.1 方法一:forEach 和 push

一个最基本的想法就是,创建一个_flat 方法用来遍历这个数组,然后再在历过程中对数组中的每一个元素进行判断;如果元素的类型不是数组则直接 push 到记录数组中去,如果元素的类型是数组,则对此内嵌数组递归调用_flat;这样相当于对任何一级的数组的每一个元素都进行了遍历,也就是使用深度遍历算法。

function _flat(targetArray, container = []) {
  if (!Array.isArray(targetArray)) return container;
  targetArray.forEach((item) => {
    if (!Array.isArray(item)) {
      container.push(item);
    } else {
      _flat(item, container);
    }
  });
  return container;
}
const rst = _flat([[[[[[1], 2], 3], 4], 5, 6], 7]);
// const rst = _flat('[[[[[[1],2],3],4],5,6],7]');
console.log("rst: ", rst);

2.2 方法二: Array.prototype.flat

ES6 中 Array 的原型上增加了一个名为 flat 的方法,其作用就是将嵌套数组拆包一次;显然没拆一次,整个数组的元素数目是(非严格)单调递增的;根据这个性质,使用 while 循环一直对其拆包,直到某两次拆完之后元素数目相等.

function _flat2(targetArray) {
  if (!Array.isArray(targetArray)) return [];
  let _loop = targetArray;
  while (1) {
    const beforeFlat = _loop.length;
    const _Arr = _loop.flat();
    const afterFlat = _Arr.length;
    if (beforeFlat == afterFlat) return _Arr;
    _loop = _Arr;
  }
}
const rst2 = _flat2([[[[[[1], 2], 3], 4], 5, 6], 7]);
console.log("rst2: ", rst2);

2.3 方法三: findIndex 和 splice

如果在遍历之前就知道为内嵌数组元素的序列号就好了,这样只需要到对应的位置上找到并将其展开就可以了;这个过程一直持续到原数组中再也找不到内嵌数组元素就停止下来。这种方法是在原来的数组上直接操作的,会改变原数组的内容

function _flat3(targetArray) {
  if (!Array.isArray(targetArray)) return [];
  while (1) {
    const arrItemIndex = targetArray.findIndex((item) => Array.isArray(item));
    if (arrItemIndex === -1) return targetArray;
    targetArray.splice(arrItemIndex, 1, ...targetArray[arrItemIndex]);
  }
}
const rst3 = _flat3([[[[[[1], 2], 3], 4], 5, 6], 7]);
console.log("rst3: ", rst3);

2.4 方法四: stack

function _flat4(targetArray) {
  if (!Array.isArray(targetArray)) return [];
  // 原始数组全部入a栈
  const a = [...targetArray];
  const b = [];
  while (a.length) {
    const _tmp = a.pop();
    if (Array.isArray(_tmp)) {
      a.push(..._tmp); // 这里不要遍历push,显得很low,a.concat(_tmp)也可
    } else {
      b.push(_tmp);
    }
  }
  return b;
}
const rst4 = _flat4([[[[[[1], 2], 3], 4], 5, 6], 7]);
console.log("rst4: ", rst4);

手写一个函数,实现数组去重的功能就是字面意思,不难理解!3.1 利用 set 对象的机制,将数组先变成 set 然后将 set 再变成数组

// 一行搞定
const unique = (array) => [...newSet(array)];

3.2 继续使用两个栈

const unique2 = (array) => {
  if (!Array.isArray(array)) return [];
  const a = [...array];
  const b = [];
  while (a.length) {
    const item = a.pop();
    // const item = a.shift();
    if (b.includes(item)) continue;
    b.push(item);
  }
  return b;
};

可以将pop改成shift有利于保证顺序

3.3 3.2 改进版本

对上面的实现方式进行优化,因为数组通过内容查询元素的效率实在是太低了,所以将 b 从栈改成字典,字典一般是使用 hash 表实现的,在根据查找方面比数组要快

const unique3 = (array) => {
  if (!Array.isArray(array)) return [];
  const a = [...array];
  const b = new Map();
  while (a.length) {
    const item = a.pop();
    // const item = a.shift();
    b.set(item, 1);
  }
  return [...b.keys()];
};
console.log(unique3([1, 1, 1, 1, 2, 3, 4, 345, 345]));

4.四句英语积累go over -- to carefully check or review somethng (review)This proposal is very important so let's [go over it] one more time.Alex, please [go over the report] and [make sure] [there are no mistakes].bring up -- to start discussing a topicThat's a very interesting point - I'm glad you [brought it up].I'm sorry, but I don't think [we have time for that]. Maybe you should [bring it up] at the next meeting.