每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。
1. 一个类型体操
类型体操题目集合 Pop
实现一个通用 Pop<T>,它接受一个数组 T ,并返回一个由数组 T 的前 length-1 项以相同的顺序组成的数组。
例如:
type arr1 = ["a", "b", "c", "d"];
type arr2 = [3, 2, 1];
type re1 = Pop<arr1>; // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2>; // expected to be [3, 2]
分析
这没什么好说的。
尝试写出
type Pop<T extends any[]> = T extends [...infer R, infer L] ? R : T;
测试用例
type re1 = Pop<arr1>; // ['a', 'b', 'c']
type re2 = Pop<arr2>; // [3, 2]
参考答案
type Pop<T extends any[]> = T extends [...infer R, infer E] ? [...R] : never;
经验总结
- 这里的
[...R]有什么特殊的含义么?
另外一个类型体操
Last of Array
实现一个通用 Last,它接受一个数组 T 并返回其最后一个元素的类型。
例如:
type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];
type tail1 = Last<arr1>; // expected to be 'c'
type tail2 = Last<arr2>; // expected to be 1
分析
这没什么好说的。
尝试写出
type Last<T extends any[]> = T extends [...infer R, infer L] ? L : never;
测试用例
type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];
type tail1 = Last<arr1>; // 'c'
type tail2 = Last<arr2>; // 1
参考答案
type Last<T extends any[]> = T extends [infer L, ...infer R]
? R["length"] extends 0
? L
: Last<R>
: never;
经验总结
- 是答案搞复杂了么?
2. 两个 Leetcode 题目
刷题的顺序参考这篇文章 LeeCode 刷题顺序
2.1 [13] 罗马数字转整数
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。
示例 1:
输入: s = "III"
输出: 3
示例 2:
输入: s = "IV"
输出: 4
示例 3:
输入: s = "IX"
输出: 9
示例 4:
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15
s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - 百度百科。
尝试实现:
/**
* @param {string} s
* @return {number}
*/
var romanToInt = function (s) {
let sum = 0;
const rec = {
I: 1,
IV: 4,
V: 5,
IX: 9,
X: 10,
XL: 40,
L: 50,
XC: 90,
C: 100,
CD: 400,
D: 500,
CM: 900,
M: 1000,
};
while (s.length) {
if (s.startsWith("CM")) {
sum += 900;
s = s.slice(2);
continue;
}
if (s.startsWith("CD")) {
sum += 400;
s = s.slice(2);
continue;
}
if (s.startsWith("XC")) {
sum += 90;
s = s.slice(2);
continue;
}
if (s.startsWith("XL")) {
sum += 40;
s = s.slice(2);
continue;
}
if (s.startsWith("IX")) {
sum += 9;
s = s.slice(2);
continue;
}
if (s.startsWith("IV")) {
sum += 4;
s = s.slice(2);
continue;
}
sum += rec[s[0]];
s = s.slice(1);
}
return sum;
};
我的思路: 不了解罗马数字可能会被吓唬住,但是其实罗马数字就是表意的,从头遍历一遍即可得到答案。
得分结果: 8.92% 13.90%
2.2 [12] 整数转罗马数字
七个不同的符号代表罗马数字,其值如下:
符号 值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:
如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I): IV ,9 是 10 (X) 减 1 (I):IX。仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
只有 10 的次方(I, X, C, M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,请使用 减法形式。
给定一个整数,将其转换为罗马数字。
示例 1:
输入:num = 3749
输出: "MMMDCCXLIX"
解释:
3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M)
700 = DCC 由于 500 (D) + 100 (C) + 100 (C)
40 = XL 由于 50 (L) 减 10 (X)
9 = IX 由于 10 (X) 减 1 (I)
注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位
示例 2:
输入:num = 58
输出:"LVIII"
解释:
50 = L
8 = VIII
示例 3:
输入:num = 1994
输出:"MCMXCIV"
解释:
1000 = M
900 = CM
90 = XC
4 = IV
提示:
1 <= num <= 3999
尝试完成:
/**
* @param {number} num
* @return {string}
*/
var intToRoman = function (num) {
const arr = (num + "").split("");
const n = arr.length;
const g = arr[n - 1];
const s = arr[n - 2] ?? "0";
const b = arr[n - 3] ?? "0";
const q = arr[n - 4] ?? "0";
let rst = "";
const qq = {
0: "",
1: "M",
2: "MM",
3: "MMM",
};
if (q) {
let _q = qq[q];
rst += _q;
}
const bb = {
0: "",
1: "C",
2: "CC",
3: "CCC",
4: "CD",
5: "D",
6: "DC",
7: "DCC",
8: "DCCC",
9: "CM",
};
if (b) {
let _b = bb[b];
rst += _b;
}
const ss = {
0: "",
1: "X",
2: "XX",
3: "XXX",
4: "XL",
5: "L",
6: "LX",
7: "LXX",
8: "LXXX",
9: "XC",
};
if (s) {
let _s = ss[s];
rst += _s;
}
const gg = {
0: "",
1: "I",
2: "II",
3: "III",
4: "IV",
5: "V",
6: "VI",
7: "VII",
8: "VIII",
9: "IX",
};
if (g) {
let _g = gg[g];
rst += _g;
}
return rst;
};
我的思路:
- 和上面那个题差不多,只要罗马数字是从左到右读的,那就和阿拉伯数字差不多;
- 考虑到最大值为 4000,我们可以先得到各个位上的数字并且不用考虑 0,因为题目给了数字的范围。
得分结果: 97.41% 25.03%
3. 六个 vue2.x 面试题
- Vue2.x 生命周期有哪些? 系统自带:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
- Vue2.x 一旦进入到页面或者组件,会执行哪些生命周期,它们的顺序又是什么?
- beforeCreate
- created
- beforeMount
- mounted
- Vue2.x 在哪个阶段有data?
- beforeCreate 啥也没有
- created 有 data 没有 el
- beforeMount 有 data 没有 el
- mounted 都有
- Vue2.x 如果加入了 keep-alive 会多俩个生命周期?
- activated
- deactivated
- Vue2.x 如果加入了 keep-alive,第一次进入组件会执行哪些生命?
- beforeCreate
- created
- beforeMount
- mounted
- activated
- 如果加入了 keep-alive,第二次或者第 N 次进入组件会执行哪些生命周期? 只执行一个生命周期:activated
4.四句英语积累
- Health or sanitary means '卫生' in Chinese。
- If we [get a chance] [maybe someday] we can [hangout together].
- [seems] you [have been to] [all places in Hangzhou].
- yeah [I went once there] [when there was a holiday].