2024.5.8 七牛云 笔试
先上大题:两道力扣中等算法题 + 一道字符串业务题
第一题:LeetCode 1361.验证二叉树
最直观的分析:
- 只能有一个节点(根节点)入度为0(即必须为一个连通图)
- 其余节点入度为1(同一个孩子不能有两个父亲)
- 不能有环
/**
* @param {number} n
* @param {number[]} leftChild
* @param {number[]} rightChild
* @return {boolean}
*/
var validateBinaryTreeNodes = function(n, leftChild, rightChild) {
let input_degree = {}
for(let i = -1; i < n; i ++){
input_degree[i] = 0
}
let root;
for(let i = 0; i < n; i ++){
input_degree[leftChild[i]] ++ input_degree[rightChild[i]] ++
}
delete input_degree[-1]
for(let key in input_degree){
if(input_degree[key] == 0){ root = +key }
}
let nodes_set = new Set()
let queue = [root]
while(queue.length != 0){
let node = queue.pop()
if(node == -1) continue
if(nodes_set.has(node)) return false
nodes_set.add(node)
queue.unshift(leftChild[node])
queue.unshift(rightChild[node])
}
if(nodes_set.size = n) return true
};
这种连通图的题进阶想到用 并查集
var validateBinaryTreeNodes = function(n, leftChild, rightChild) {
function UF(n){
this.parents = [...new Array(n)].map((_, i) => i)
}
UF.prototype.find = function(idx){
if(this.parents[idx] !== idx){
this.parents[idx] = this.find(this.parents[idx])
}
return this.parents[idx]
}
UF.prototype.union = function(child, father){
this.parents[this.find(child)] = this.find(father)
}
const uf = new UF(n)
for(let i = 0; i < n; i ++){
for(let child of [leftChild[i], rightChild[i]]){
if(child == -1) continue
//保证只有一个父亲
if(uf.find(child) !== child) return false
//保证无环
if(uf.find(i) === child) return false
//构建父子关系
uf.union(child, i)
}
}
//保证只有一个老祖
const root = uf.find(0)
for(let i = 1; i < n; i ++){
if(uf.find(i) !== root) return false
}
return true
};
笔试那道题要比力扣这道题多判断一个东西,注意一下就行:每个节点出度小于3
———————————————————————————————————————————————
第二题:LeetCode 1574.删除最短子数组使剩余数组有序
开始想法是最长连续子数组那种题的做法(dp),但好像这道题不能这样做
双指针? (我觉得这道题单调栈用的没必要)
O(n^2)解法 (这个解法笔试时候该很容易想到的,脑子宕机了🥶)
var findLengthOfShortestSubarray = function(arr) {
let i = 0
let j = arr.length-1
while(i < arr.length-1 && arr[i+1] >= arr[i]){
i ++
}
while(j > 0 && arr[j] >= arr[j-1]){
j --
}
if(i == arr.length-1) return 0
let longest = Math.max(i+1, arr.length-j)
for(let k = 0; k <= i; k++){
let start = k
let end = j
while (end < arr.length && arr[end] < arr[start]) {
end++;
}
longest = Math.max(longest, start + 1 + arr.length - end);
}
return arr.length-longest
};
优化到O(n)
var findLengthOfShortestSubarray = function(arr) {
let len = arr.length, j = len-1
while(j > 0 && arr[j] >= arr[j-1]){
j --
}
if(j == 0) return 0
let res = j
for(let i = 0; i < j; i ++){
while(j < len && arr[j] < arr[i]){
j ++
}
res = Math.min(res, j-i-1)
if(arr[i+1] < arr[i]) break
}
return res
};
——————————————————————————————————————————————————————————————————-
第三题
实现一个字符串替换方法,把配对的大括号内特定格式的字符串替换为对应数据的内容,不匹配的不做替换:。替换前的字符串里只有空格、数字、英语大小写字母、逗号、句号和大括号,大括号总是配对出现并且大括号不会嵌套出现。
JSON数据内部,对象key由英语小写字母组成,value由对象、字符串和数字组成 (没有数组和boolean和null)。JSON数据可能会有多层,在大括号中通过驼峰法则一一对应。
样例输入输出
这种题一看就头大,但拆成几个方法分别实现就清晰了
- keysToReplace(s) 提取出待替换的驼峰字符串
- toKeysArr(key) 将拿到的驼峰字符串变成小写单词数组
- search(key,obj) 根据键名在obj里搜索键值
- myReplace(reg, s) 进行替换操作
对于第一步,讲两点:
首先,可以直接用用正则拿到(我背不住正则)
const regex = /{([^}]+)}/g;
const totalKeys = sentence.match(regex).map(item => item.slice(1, -1).trim());
其次,用我后面暴力解法是有个bug的,关于for in 遍历出问题的,详情见我另一篇文章 juejin.cn/post/736686… ,希望有前辈能帮我解答下🙏
上代码:
//测试数据
const sentence =
"My name is {userName}, { userAge } years old. I am a {userJobName} and I have a {not_exist}.";
const jsonData = {
user: {
name: "John",
age: 30,
job: {
name: "Software Engineer",
},
},
};
function parseSentence(sentence, jsonData) {
//1.从sentence中提取出所有{}中的内容
let totalKeys = keysToReplace(sentence);
// console.log("totalkeys:", totalKeys);
//2.将key转换为数组(比如userName -> ['user', 'name'])
let finalKeys = totalKeys.map(item => toKeysArr(item));
// console.log("finalKeys:", finalKeys);
//3.根据key在jsonData中查找对应的值
let values = finalKeys.map((keys) => search(keys, jsonData));
// console.log("values:", values);
//4.替换操作
const regex = /{([^}]+)}/g;
const newSentence = myReplace(regex, sentence)
console.log(newSentence);
function keysToReplace(s){
let idx = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] == "{") idx = i;
else if (s[i] == "}") {
totalKeys.push(s.slice(idx + 1, i).trim());
}
}
}
function toKeysArr(key) {
let keys = [];
let upperIdx = [];
for (let i in key) {
if (isUpperCase(key[i])) {
upperIdx.push(i);
}
}
if (upperIdx.length != 0) {
keys.push(key.slice(0, upperIdx[0]).toLowerCase());
for (let i = 0; i < upperIdx.length - 1; i++) {
keys.push(
key.slice(upperIdx[i], upperIdx[i + 1]).toLowerCase()
);
}
keys.push(key.slice(upperIdx[upperIdx.length - 1]).toLowerCase());
}
return keys;
}
function search(keys, obj) {
if (keys.length == 0) return "nonono";
let copy_obj = JSON.parse(JSON.stringify(obj));
for (let i = 0; i < keys.length; i++) {
if (Object.keys(copy_obj).includes(keys[i])) {
copy_obj = copy_obj[keys[i]];
} else {
return "nonono";
}
}
return copy_obj;
}
function myReplace(reg, s){
let value_idx = 0;
return s.replace(regex, (match) => {
if (values[value_idx] != "nonono") {
return values[value_idx++];
} else {
return match;
}
});
}
//太坑了,JS里没有直接的isUpperCase方法🥶
function isUpperCase(letter) {
return (
letter === letter.toUpperCase() && letter !== letter.toLowerCase()
);
}
}
parseSentence(sentence, jsonData);
———————————————————————————————————————————————
接下来是一些零碎的小题知识点,做下来确实有点后悔计网、计组啥的没好好学以及深感自己的JS基础不扎实
-
splice(start, [deleteCount], item1, item2, ..) 考试的时候用成了splice(start,end,item) 基础不扎实
-
我竟然写出了
if (keys[i] in Object.keys(copy_obj))和for(let i in s.length)这样的代码,看来我还是对python爱的深沉😇 -
字符串只有slice方法,没有splice方法!
4.OSI七层模型及对应相关协议(笔试它考我的是IP,DNS,TCP,SSL哪个协议在最下面)
补充:SSL/TLS在应用层(其实是和应用层之间)
5.浮点数和定点数, 原码、反码、补码,有符号整数、无符号整数 等知识
个人觉得正确答案是ACD
- 浏览器环境中Web应用是无法获取设备的MAC地址的
- 对于https请求,中间人也是可以得到请求的目标服务器的ip信息的。
- 通过http请求下载1GB的文件,产生的网络流量可能小于1GB(压缩、缓存)
- http的content-length是body的长度,不包含header
- http中有HEAD方法,与GET只请求页面的首部信息,而不返回页面的内容主体
-
DNS考察。选2、3、4
-
跨域考察。选B、C、D
A:http默认80端口(80与8080有区别??这个我不确定)
B:postMessage方法是不受同源策略限制的, 详细介绍见:developer.mozilla.org/zh-CN/docs/…
C:同源,不影响
D:script标签不受同源策略限制(JSONP)(这里有不确定,HTTPS和HTTP之间的协议差异应该不会触发同源策略的限制??)
- Promise考察。选B、D
这道题首先排除A(getData没有async标记);其次对于Promise抛出的错误,then的第二个参数(err => {},这里用了简写语法,相当于err => {console.log(err)})和catch都能捕获到(但只能捕获一次);对于C选项,getData()函数返回的是一个 Promise,因为 Promise 中的错误是异步的,而 try...catch 块只能捕获同步代码块中的异常。
10.css选择器权重考察。 内联>id>类=属性=伪类>元素=伪元素>通用
———————————————————————————————————————————————
同时笔试还考察了二叉树的前中后序遍历(给你前中让你写后)、时间复杂度、闭包等内容,题量不大但还是比较全面的。
这是我的第一次笔试,吸取教训吧