每日的知识积累,包括 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%
总结提升:
- 做了一个简单的缓存通过了测试,性能更好的缓存策略需要更强的算法知识,现在先跳过。
- 当题目要求中出现
最多调用 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;
};
我的思路:
- 按照数组中的 0 分类讨论;如果有两个或以上的 0 则直接返回全零数组;如果恰好有一个 0 则原来 0 的位置为其他元素乘积;如果没有 0 则元素值等于所有元素乘积除以原来元素的值。
- 注意上面的这些信息可以通过一次遍历获取到。
得分结果: 48.80% 98.94%
总结提升:
- 题目没说不可变数组或者矩阵的,我们可以修改原数组,以获取更低的内存消耗。
3. 三个前端题目
- 列举常见的正则表达式
-
- 16 进制的颜色值:
/#([0-9a-zA-Z]{6}|[0-9a-zA-Z]{3})/g
- 16 进制的颜色值:
-
- 匹配日期:
/^(0-9{4})-(0[1-9]|1[0-2])-(0[1-9][12][0-9]|3[0-1])$/g
- 匹配日期:
-
- 匹配 qq 号码:
/^[1-9][0-9]{4,10}$/g
- 匹配 qq 号码:
-
- 匹配手机号码:
/^1[34578]\d{9}$/g
- 匹配手机号码:
-
- 常见的用户名:
/^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/g
- 常见的用户名:
- 实现 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);
}
}
- 实现 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.四句英语积累
- point something out -- to tell someone something that they did not already know or had not thought about
- [That's completely new to me! Thanks for pointing it out].
- I'd just like to [point out that] these results [are from last year, not this year].
- think something through -- to carefully think about the possible result of doing something
- [I'm afraid that wasn't our best decision. We just didn't think it through].
- [I'm sorry, but I don't think this plan will work. You and your team need to think it through again].