笔试记录(1)

147 阅读6分钟

2024.5.8 七牛云 笔试

先上大题:两道力扣中等算法题 + 一道字符串业务题

第一题:LeetCode 1361.验证二叉树

最直观的分析:

  1. 只能有一个节点(根节点)入度为0(即必须为一个连通图)
  2. 其余节点入度为1(同一个孩子不能有两个父亲)  
  3. 不能有环
/** 
* @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数据可能会有多层,在大括号中通过驼峰法则一一对应。

样例输入输出 截屏2024-05-10 14.19.02.png

这种题一看就头大,但拆成几个方法分别实现就清晰了

  1. keysToReplace(s) 提取出待替换的驼峰字符串
  2. toKeysArr(key) 将拿到的驼峰字符串变成小写单词数组
  3. search(key,obj) 根据键名在obj里搜索键值
  4. 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基础不扎实

  1. splice(start, [deleteCount], item1, item2, ..) 考试的时候用成了splice(start,end,item) 基础不扎实

  2. 我竟然写出了if (keys[i] in Object.keys(copy_obj))for(let i in s.length) 这样的代码,看来我还是对python爱的深沉😇

  3. 字符串只有slice方法,没有splice方法!

4.OSI七层模型及对应相关协议(笔试它考我的是IP,DNS,TCP,SSL哪个协议在最下面)

image.png 补充:SSL/TLS在应用层(其实是和应用层之间)

5.浮点数和定点数, 原码、反码、补码,有符号整数、无符号整数 等知识

截屏2024-05-09 00.30.59.png 个人觉得正确答案是ACD

  • 浏览器环境中Web应用是无法获取设备的MAC地址的
  • 对于https请求,中间人也是可以得到请求的目标服务器的ip信息的。
  • 通过http请求下载1GB的文件,产生的网络流量可能小于1GB(压缩、缓存)
  • http的content-length是body的长度,不包含header
  • http中有HEAD方法,与GET只请求页面的首部信息,而不返回页面的内容主体
  1. DNS考察。选2、3、4 截屏2024-05-10 15.30.25.png

  2. 跨域考察。选B、C、D

A:http默认80端口(80与8080有区别??这个我不确定)

截屏2024-05-10 15.52.18.png

B:postMessage方法是不受同源策略限制的, 详细介绍见:developer.mozilla.org/zh-CN/docs/…

C:同源,不影响

D:script标签不受同源策略限制(JSONP)(这里有不确定,HTTPS和HTTP之间的协议差异应该不会触发同源策略的限制??) image.png

  1. Promise考察。选B、D

这道题首先排除A(getData没有async标记);其次对于Promise抛出的错误,then的第二个参数(err => {},这里用了简写语法,相当于err => {console.log(err)})和catch都能捕获到(但只能捕获一次);对于C选项,getData()函数返回的是一个 Promise,因为 Promise 中的错误是异步的,而 try...catch 块只能捕获同步代码块中的异常。

截屏2024-05-09 00.09.41.png

10.css选择器权重考察。 内联>id>类=属性=伪类>元素=伪元素>通用

———————————————————————————————————————————————

同时笔试还考察了二叉树的前中后序遍历(给你前中让你写后)、时间复杂度、闭包等内容,题量不大但还是比较全面的。

这是我的第一次笔试,吸取教训吧