每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。
1. 一个类型体操
Replace All
实现 ReplaceAll<S, From, To> 将一个字符串 S 中的所有子字符串 From 替换为 To。
分析
这个就是对字符串类型进行遍历。
尝试写出
type ReplaceAll<T extends string, A extends string, B extends string> = T extends `${infer F}${infer R}` ? (
F extends A ? `${B}${ReplaceAll<R,A,B>}` : `${F}${ReplaceAll<R,A,B>}`
) : T;
测试用例
type replaced = ReplaceAll<'t y p e s', ' ', ''> // 期望是 'types'
参考答案
type ReplaceAll<S extends string, From extends string, To extends string> = From extends ''
? S
: (S extends `${infer L}${From}${infer R}`
? `${ReplaceAll<L, From, To>}${To}${ReplaceAll<R, From, To>}`
: S);
再来一个类似的类型体操
Replace
实现 Replace<S, From, To> 将字符串 S 中的第一个子字符串 From 替换为 To 。
分析
这次的话就是第一次就停了,我们不用遍历了,可以使用更加简单的方式。
尝试写出
type Replace<T extends string, A extends string, B extends string> = T extends `${infer F}${A}${infer L}` ? `${F}${B}${L}` : T;
测试用例
type replaced = Replace<'types are fun!', 'fun', 'awesome'> // 期望是 'types are awesome!'
参考答案
type Replace<S extends string, From extends string, To extends string> = From extends ''
? S
: (S extends `${infer L}${From}${infer R}`
? (`${L}${To}${R}`)
: S);
经验总结
- 注意这里的标准答案对
''做了特殊处理,确实比较严谨。
2. 四个 Leetcode 题目
刷题的顺序参考这篇文章 LeeCode 刷题顺序
2.1 [506] 相对名次
给你一个长度为 n 的整数数组 score ,其中 score[i] 是第 i 位运动员在比赛中的得分。所有得分都 互不相同 。
运动员将根据得分 决定名次 ,其中名次第 1 的运动员得分最高,名次第 2 的运动员得分第 2 高,依此类推。运动员的名次决定了他们的获奖情况:
名次第 1 的运动员获金牌 "Gold Medal" 。
名次第 2 的运动员获银牌 "Silver Medal" 。
名次第 3 的运动员获铜牌 "Bronze Medal" 。
从名次第 4 到第 n 的运动员,只能获得他们的名次编号(即,名次第 x 的运动员获得编号 "x")。
使用长度为 n 的数组 answer 返回获奖,其中 answer[i] 是第 i 位运动员的获奖情况。
示例 1:
输入:score = [5,4,3,2,1]
输出:["Gold Medal","Silver Medal","Bronze Medal","4","5"]
解释:名次为 [1st, 2nd, 3rd, 4th, 5th] 。
示例 2:
输入:score = [10,3,8,9,4]
输出:["Gold Medal","5","Bronze Medal","Silver Medal","4"]
解释:名次为 [1st, 5th, 3rd, 2nd, 4th] 。
提示:
n == score.length
1 <= n <= 104
0 <= score[i] <= 106
score 中的所有值 互不相同
尝试实现:
/**
* @param {number[]} score
* @return {string[]}
*/
var findRelativeRanks = function(score) {
const sorted = [...score];
score.sort((a,b)=>b-a);
score.forEach((v, i)=> {
const idx = sorted.findIndex(u => u===v);
let now = i+1+"";
if(i===0) now="Gold Medal";
if(i===1) now="Silver Medal";
if(i===2) now="Bronze Medal";
sorted[idx] = now;
})
return sorted;
};
我的思路:
- 先复制一份,然后排序,最后查找变值即可。
得分结果: 6.42% 59.64%
总结提升:
- 数组上面的 sort 函数会改变原来的数组。
- 在 sort 之前使用
[...]保留一份副本。
2.2 [535] TinyURL 的加密与解密
TinyURL 是一种 URL 简化服务, 比如:当你输入一个 URL https://leetcode.com/problems/design-tinyurl 时,它将返回一个简化的URL http://tinyurl.com/4e9iAk 。请你设计一个类来加密与解密 TinyURL 。
加密和解密算法如何设计和运作是没有限制的,你只需要保证一个 URL 可以被加密成一个 TinyURL ,并且这个 TinyURL 可以用解密方法恢复成原本的 URL 。
实现 Solution 类:
Solution() 初始化 TinyURL 系统对象。
String encode(String longUrl) 返回 longUrl 对应的 TinyURL 。
String decode(String shortUrl) 返回 shortUrl 原本的 URL 。题目数据保证给定的 shortUrl 是由同一个系统对象加密的。
示例:
输入:url = "https://leetcode.com/problems/design-tinyurl"
输出:"https://leetcode.com/problems/design-tinyurl"
解释:
Solution obj = new Solution();
string tiny = obj.encode(url); // 返回加密后得到的 TinyURL 。
string ans = obj.decode(tiny); // 返回解密后得到的原本的 URL 。
提示:
1 <= url.length <= 104
题目数据保证 url 是一个有效的 URL
尝试完成:
/**
* Encodes a URL to a shortened URL.
*
* @param {string} longUrl
* @return {string}
*/
var encode = function(longUrl) {
return encodeURI(longUrl)
};
/**
* Decodes a shortened URL to its original URL.
*
* @param {string} shortUrl
* @return {string}
*/
var decode = function(shortUrl) {
return decodeURI(shortUrl)
};
/**
* Your functions will be called as such:
* decode(encode(url));
*/
我的思路:
- 使用内置的加解密算法即可。
得分结果: 85.19% 85.19%
2.3 [299] 猜数字游戏
你在和朋友一起玩 猜数字(Bulls and Cows)游戏,该游戏规则如下:
写出一个秘密数字,并请朋友猜这个数字是多少。朋友每猜测一次,你就会给他一个包含下述信息的提示:
猜测数字中有多少位属于数字和确切位置都猜对了(称为 "Bulls",公牛),
有多少位属于数字猜对了但是位置不对(称为 "Cows",奶牛)。也就是说,这次猜测中有多少位非公牛数字可以通过重新排列转换成公牛数字。
给你一个秘密数字 secret 和朋友猜测的数字 guess ,请你返回对朋友这次猜测的提示。
提示的格式为 "xAyB" ,x 是公牛个数, y 是奶牛个数,A 表示公牛,B 表示奶牛。
请注意秘密数字和朋友猜测的数字都可能含有重复数字。
示例 1:
输入:secret = "1807", guess = "7810"
输出:"1A3B"
解释:数字和位置都对(公牛)用 '|' 连接,数字猜对位置不对(奶牛)的采用斜体加粗标识。
"1807"
|
"7810"
示例 2:
输入:secret = "1123", guess = "0111"
输出:"1A1B"
解释:数字和位置都对(公牛)用 '|' 连接,数字猜对位置不对(奶牛)的采用斜体加粗标识。
"1123" "1123"
| or |
"0111" "0111"
注意,两个不匹配的 1 中,只有一个会算作奶牛(数字猜对位置不对)。通过重新排列非公牛数字,其中仅有一个 1 可以成为公牛数字。
提示:
1 <= secret.length, guess.length <= 1000
secret.length == guess.length
secret 和 guess 仅由数字组成
尝试完成:
/**
* @param {string} secret
* @param {string} guess
* @return {string}
*/
var getHint = function(secret, guess) {
const _s = secret.split("");
const _g = guess.split("");
const n = Math.min(_s.length,_g.length);
let A = 0;
for(let i = 0; i < n; i++) {
if(_s[i] === _g[i]){
A++;
_s[i] = null;
_g[i] = null;
}
}
let B = 0;
const reds = {};
const redg = {};
for(let i = 0; i < n; i++) {
const scur = _s[i];
if( scur !== null){
if(typeof reds[scur] === 'undefined'){
reds[scur] = 1;
} else {
reds[scur] += 1;
}
}
const gcur = _g[i];
if( gcur !== null){
if(typeof redg[gcur] === 'undefined'){
redg[gcur] = 1;
} else {
redg[gcur] += 1;
}
}
}
for(let j in redg) {
if(typeof reds[j] !== "undefined") {
B += Math.min(redg[j], reds[j]);
}
}
return `${A}A${B}B`
};
我的思路:
- 先变成数组,这样的话就可以使用已有的 api 了
- 获取最小长度,进行遍历
- 将数字对,位置也对的筛选出去
- 统计剩余数组中的字符,取最小值相加
得分结果: 46.91% 25.92%
**总结提升:**将字符串变成数组的代价有点大,需要更好的方式。
2.4 [412] Fizz Buzz
给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:
answer[i] == "FizzBuzz" 如果 i 同时是 3 和 5 的倍数。
answer[i] == "Fizz" 如果 i 是 3 的倍数。
answer[i] == "Buzz" 如果 i 是 5 的倍数。
answer[i] == i (以字符串形式)如果上述条件全不满足。
示例 1:
输入:n = 3
输出:["1","2","Fizz"]
示例 2:
输入:n = 5
输出:["1","2","Fizz","4","Buzz"]
示例 3:
输入:n = 15
输出:["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz"]
尝试完成:
/**
* @param {number} n
* @return {string[]}
*/
var fizzBuzz = function(n) {
const rst = [];
if(n===0) return rst;
for(let i = 0; i < n; i++) {
if((i+1) % 5 === 0 && (i+1) % 3 === 0) {
rst[i] = "FizzBuzz";
continue;
}
if((i+1) % 3 === 0) {
rst[i] = "Fizz";
continue;
}
if((i+1) % 5 === 0) {
rst[i] = "Buzz";
continue;
}
rst[i] = i+1+"";
}
return rst;
};
得分结果: 92.89% 27.56%
总结提升:
- 下标和自然数的关系要搞清楚。
- 先判断条件严苛的,后判断松的,就像设置路由一样。
3. 三个前端题目
- 实现Array.prototype.unshift
原理:unshift() 方法将一个或多个元素添加到数组的开头,并返回新数组的长度。
实现:分别对原来数组和新增加的元素序列进行遍历,前者的作用是为了挪动元素的顺序,后者的作用是为了插入新数据
返回值:变化之后的数组的长度
function myUnshift (...eles) {
if(!Array.isArray(this)) throw new Error('must be called by array');
const _count = eles?.length ?? 0;
const _len = this.length;
if(_count == 0) return _len;
for (let i = 0; i < _len; i++) {
this[i+_count] = this[i];
}
for (let i = 0; i < _count; i++) {
this[i] = eles[i];
}
return this.length;
}
- 实现Array.prototype.slice
原理:slice() 方法返回原始数组的指定部分(浅拷贝),不修改原始数组。 实现:实现的难点在于对负号下标的处理上 返回值:返回切片元素组成的数组.
function mySlice (start, end) {
if(!Array.isArray(this)) throw new Error('must be called by array');
let _start = start === undefined ? 0 : start;
let _end = end === undefined ? this.length : end;
if(typeof _start !== 'number' || Number.isNaN(_start)) throw new Error('start must be number');
if(typeof _end !== 'number' || Number.isNaN(_end)) throw new Error('end must be number');
const _result = [];
if (_start > this.length) {
return _result; // 起始位置超出数组长度时返回空数组
}
if (_start < 0) {
_start = Math.max(this.length + _start, 0); // 将负数的起始位置转换为正数
}
if(_end < 0) {
_end = Math.max(this.length + _end, 0); // 将负数的终止位置转换为正数
}
if (_end > this.length) {
_end = this.length; // 结束位置超出数组长度时将其修正为数组长度
}
if (_start > _end) {
return _result;
}
for (let i = _start; i < _end; i++>) {
_result.push(this[i]);
}
return _result;
}
- 实现Array.prototype.splice 原理:splice() 方法从数组中删除、替换或插入新的元素,并返回被删除的元素数组。
实现:使用一个临时变量保存原来数组中的元素,然后将其分成三个部分,然后按照要求拼接;需要注意的是:使用.length = 0来清除数组中的元素,而不要直接给数组赋值!
返回值:返回被删除的元素组成的数组
function mySplice (start, deleteCount = 0, ...items) {
if(!Array.isArray(this)) throw new Error('must be called by array');
let _tmp = [...this];
// 从0到start-1
const fragment1 = _tmp.slice(0, start-1);
// 从start到deleteCount-1
const fragment2 = _tmp.slice(start, deleteCount-1);
// 从deleteCount到末尾
const fragment3 = _tmp.slice(deleteCount);
_tmp = [...fragment1, ...items, ...fragment3];
this.length = 0;
for (let i = 0; i < _tmp.length; i++) {
this[i] = _tmp[i];
}
return fragment2;
}
4.四句英语积累
- I can understand that you're angry, but there's no need to get personal - I'm just tring to help.
- I completely understand why you're so upset, but I don't think it's fair to speak to me like that - I'm just doing my job.
- Could you please tone down your language, sir/madam?
- I don't want to help you, but I'm afraid I can't tolerate the kind of language you're using.