每日知识积累 Day 14

113 阅读6分钟

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

1. 一个类型体操

类型体操题目集合

Append to object

实现一个为接口添加一个新字段的类型。该类型接收三个参数,返回带有新字段的接口类型。

例如:

type Test = { id: "1" };
type Result = AppendToObject<Test, "value", 4>; // expected to be { id: '1', value: 4 }

分析

我最初理解的就是构建一个新的 object 类型,然后使用 & 将它们连接起来。

尝试写出

type AppendToObject<T extends {}, K extends PropertyKey, V> = {
  [P in K]: V;
} & T;

测试用例

type Test = { id: "1" };

type Result = AppendToObject<Test, "value", 4>;

/**
  {
    value: 4;
  } & Test
*/

参考答案

type AppendToObject<T extends object, U extends string, V> = {
  [P in keyof T | U]: P extends keyof T ? T[P] : V;
};

经验总结

参考答案中的:

[P in (keyof T | K)]

写的非常的好。这表明,在 key 中可以使用联合类型。

2. 两个 Leetcode 题目

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

2.1 [304] 二维区域和检索 - 矩阵不可变

给定一个二维矩阵 matrix,以下类型的多个请求:

计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。
实现 NumMatrix 类:

NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
int sumRegion(int row1, int col1, int row2, int col2) 返回 左上角 (row1, col1) 、右下角 (row2, col2) 所描述的子矩阵的元素 总和 。


示例 1:



输入:
["NumMatrix","sumRegion","sumRegion","sumRegion"]
[
  [[[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]],
  [2,1,4,3],
  [1,1,2,2],
  [1,2,2,4]
]
输出:
[null, 8, 11, 12]

解释:
NumMatrix numMatrix = new NumMatrix([[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]);
numMatrix.sumRegion(2, 1, 4, 3); // return 8 (红色矩形框的元素总和)
numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和)
numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)


提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 200
-105 <= matrix[i][j] <= 105
0 <= row1 <= row2 < m
0 <= col1 <= col2 < n
最多调用 104 次 sumRegion 方法

尝试实现:

/**
 * @param {number[][]} matrix
 */
var NumMatrix = function (matrix) {
  this.cache = {};
  this.m = matrix;
};

/**
 * @param {number} row1
 * @param {number} col1
 * @param {number} row2
 * @param {number} col2
 * @return {number}
 */
NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) {
  let sum = 0;
  const key = `${row1}&${col1}&${row2}&${col2}`;
  if (this.cache[key]) return this.cache[key];
  for (let i = row1; i <= row2; i++) {
    for (let j = col1; j <= col2; j++) {
      sum += this.m[i][j];
    }
  }
  this.cache[key] = sum;
  return sum;
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * var obj = new NumMatrix(matrix)
 * var param_1 = obj.sumRegion(row1,col1,row2,col2)
 */

我的思路:

  • 一开始我没有发现难点在什么地方,后来提交之后不通过,意识到需要做缓存。

得分结果: 5.26% 30.70%

总结提升:

  1. 做了一个简单的缓存通过了测试,性能更好的缓存策略需要更强的算法知识,现在先跳过。
  2. 当题目要求中出现 最多调用 104 次 sumRegion 方法 的时候就需要注意缓存的问题了。

2.2 [238] 除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]


提示:

2 <= nums.length <= 105
-30 <= nums[i] <= 30
保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内


进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)

尝试完成:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function (nums) {
  const n = nums.length;
  let multiply = 1;
  let count0 = 0;
  let position;
  for (let i = 0; i < n; i++) {
    if (nums[i] === 0) {
      count0++;
      position = i;
    } else {
      multiply *= nums[i];
    }
  }

  if (count0 >= 2) return Array(n).fill(0);
  if (count0 === 1) {
    const nums = Array(n).fill(0);
    nums[position] = multiply;
    return nums;
  }
  for (let i = 0; i < n; i++) {
    nums[i] = multiply / nums[i];
  }

  return nums;
};

我的思路:

  1. 按照数组中的 0 分类讨论;如果有两个或以上的 0 则直接返回全零数组;如果恰好有一个 0 则原来 0 的位置为其他元素乘积;如果没有 0 则元素值等于所有元素乘积除以原来元素的值。
  2. 注意上面的这些信息可以通过一次遍历获取到。

得分结果: 48.80% 98.94%

总结提升:

  1. 题目没说不可变数组或者矩阵的,我们可以修改原数组,以获取更低的内存消耗。

3. 三个前端题目

  1. 列举常见的正则表达式
    1. 16 进制的颜色值:/#([0-9a-zA-Z]{6}|[0-9a-zA-Z]{3})/g
    1. 匹配日期:/^(0-9{4})-(0[1-9]|1[0-2])-(0[1-9][12][0-9]|3[0-1])$/g
    1. 匹配 qq 号码:/^[1-9][0-9]{4,10}$/g
    1. 匹配手机号码:/^1[34578]\d{9}$/g
    1. 常见的用户名:/^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/g
  1. 实现 Array.prototype.forEach
  • forEach 的原理是对数组中的每一个元素都通过传入的 callback 函数进行处理
  • 通过手写 forEach 就可以看出来为什么 return 和 break 是无效的
function myForEach(cb) {
  if (!Array.isArray(this)) throw new Error("must be called by array");
  const _len = this.length;
  for (let i = 0; i < _len; i++) {
    cb(this[i], i, this);
  }
}
  1. 实现 Array.prototype.some
  • 对于 some 的实现不通过 for 的遍历,而是通过 while 循环和栈数据结果来做,这样可以使整个过程更加的形象
  • 将原始数组的所有元素全部入栈,然后使用 while 逐个出栈,弹出的元素接受检查码如果通过则整体立刻返回 true,如果通不过则下一个
  • 直到栈空,则返回 false
function mySome(test) {
  if (!Array.isArray(this)) throw new Error("must be called by array");
  const _stack = [...this];
  while (_stack.length) {
    const _value = _stack.pop();
    if (test(_value, _stack.length - 1, _stack)) return true;
  }
  return false;
}

4.四句英语积累

  1. point something out -- to tell someone something that they did not already know or had not thought about
    1. [That's completely new to me! Thanks for pointing it out].
    2. I'd just like to [point out that] these results [are from last year, not this year].
  2. think something through -- to carefully think about the possible result of doing something
    1. [I'm afraid that wasn't our best decision. We just didn't think it through].
    2. [I'm sorry, but I don't think this plan will work. You and your team need to think it through again].