面试中遇到的那些手撕代码题

158 阅读3分钟

手写js各种函数

防抖节流(多个面试)

防抖

单位时间内频繁的触发事件,只执行最后一次

下面是最简单的写法

function debounce(fn, t){
    let timeid;
    return function(args){
        if(timeid){
            clearTimeout(timeid)
        }
        timeid = setTimeout(()=>{
            fn()
        }, t)
    }
}

如何获取fn的参数

function debounce(fn, t){
    let timeid;
    return function(args){
        let argu = arguments; //获取参数
        if(timeid){
            clearTimeout(timeid)
        }
        timeid = setTimeout(()=>{
            fn.apply(this, argu)
        }, t)
    }
}
debounce(fun, 100)(argu1, argu2);

节流

单位时间内频繁的触发事件,只执行第一次

function throttle(fn, t){
    let timeid;
    return function(args){
    
        let argu = arguments; //获取参数
        if(timeid){
        }else{
            timeid = setTimeout(()=>{
                fn.apply(this, argu)
                timeid = null //清空计时器
            }, t)
        }
    }
}
debounce(fun, 100)(argu1, argu2);

数组拍平

递归调用

function fn(arr){
    let res = []
    arr.forEach((val) => {
        if(val instanceof Array){
            res = res.concat(fn(val))
        }else{
            res.push(val)
        }
    })
    return res;
}

reduce和递归

function fn(arr){
    return arr.reduce((prev, cur)=>{
        return prev.concat(Array.isArray(cur)? fn(cur):cur)
    }, [])
}

reduce方法

arr.reduce((prev,cur,index,arr)=>{

},init)
  • arr: 表示原数组
  • prev:表示上一次调用回调时的返回值,或者初始值init
  • cur:表示当前正在处理的数组元素
  • index:表示正在处理的数组元素的索引,若提供init值,则索引为0,否则索引为1,此时prev为第0个元素,cur为第一个元素
  • init: 表示初始值

flat

ES6新增的嵌套数组转一维数组的方法flat

flat(n)

如果不传递参数n的话,flat函数默认只展开一层;n的值为多少就展开多少层;如果想无论多少层都展开为一维数组,则将参数值设为Infinity。flat函数在拍平过程中还会自动跳过数组中的空位。

arr.flat(Infinity)

算法题

动态规划

斐波那契数列(途虎养车、去哪儿一面)

使用动态优化写出斐波那契数列后,还要考虑空间复杂度的优化

回溯

二叉树中和为目标值的路径(百度一面)

LCR 153. 二叉树中和为目标值的路径

题目

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} target
 * @return {number[][]}
 */
var pathTarget = function(root, target) {
    // 存储结果的数组
    const res = [];
    // 回溯用的栈
    const stack = [];
    // 开始回溯
    root && backTrack(root, target, stack, 0, res);
    return res;
};
const backTrack = (node, targetSum, stack, sum, res) => {
    // 将节点入栈
    stack.push(node.val);
    // 节点加入到sum中
    sum += node.val;
    // 到达了叶子节点,并且当前记录的sum正好满足条件
    if (!node.left && !node.right && sum === targetSum) {
        // 将栈中的节点拷贝一份,推入返回结构res中
        // 若不拷贝的话,推入的是引用,stack改变,res里的stack也会改变
        res.push([...stack]);
    }
    // 如果存在左节点,继续遍历左子树,右子树同理
    node.left && backTrack(node.left, targetSum, stack, sum, res);
    node.right && backTrack(node.right, targetSum, stack, sum, res);
    // 向上回溯,stack弹出一个节点
    // 这里sum没有回溯是因为backTrack调用时将每一次的sum传入了函数中,递归函数返回后还是原本的值
    stack.pop();
};

链表

翻转链表(百度一面)

206. 反转链表 - 力扣(LeetCode)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

var reverseList = function(head) {
    let p1 = head, p2 = null
    let temp = null
    while(p1){
        temp = p1.next
        p1.next = p2
        p2 = p1
        p1 = temp
    }
    return p2
}

场景题

React里面实现count每一秒加一

function Counter(){
    const [count, setCount] = useState(0)
    useEffect(()=>{
        const timeId = setInterval(()=>{
            setCount(t => t+1) //setCount函数可以传入一个函数,来控制如何更改count,这样不会依赖count
        }, 1000);
        return ()=> clearInterval(timeId);
    }, [])
    return  <div>{count}</div>;
}