一道面试题引发的思考

206 阅读2分钟

一道面试题引发的思考

最近复习算法,正好想到了之前的一道面试题,由此引发了许多思考。在此记录一下,供以后开发时参考,希望对其他人也有用。

一道面试题

面试题如下:

// 要求实现一个 flatten 函数,有如下效果:
// [1,2,[3,4,[5,6,7],8],9]  =>  [1,2,3,4,5,6,7,8,9]
// todo

一般的解法是使用递归:

function flatten(arr) {
    return arr.reduce((accu, curr) => Array.isArray(curr) ? accu.push(...flatten(curr)) : accu.push(curr));
}

当然还有比较简便的方法:

function flatten(arr) {
    return arr.toString().split(',').map(item => parseInt(item));
}

DFS

DDFS 的全称是 Deep First Search,也叫深度优先遍历,它是先一遍遍,从上到下,从左到右,先遍历到叶子节点,然后进行回溯遍历其它节点的遍历方法。其模板形式为:

// 递归版
function dfs(node, visited) {
    visited.add(node);

    // process corrent node here
    // ...

    node.children.forEach((child) => {
        if (!visited.has(child)) {
            dfs(child, visited);
        }
    });
}

// 遍历版
function dfs(tree) {
    const visited = new Set();
    cosnt stack = [tree.root];

    while (stack.length) {
        const node = stack.shift();

        if (!visited.has(node)) {
            visited.add(node);

            // process corrent node here
            // ...

            stack.unshift(...node.children);
        }
    }
}

注意:

  1. 递归版在递归函数里面储存了状态,而遍历版使用了一个栈来储存状态。
  2. visited 是一个集合,用来检查是否已经访问过了,一般情况下我们用不到这个。

面试题的遍历写法

其实仔细一想,上面的面试题不就是深度优先遍历吗?而上面的递归写法可以转化成遍历写法,那么我们面试题的递归写法能不能转化为递归写法呢?答案是能的,直接套用模板即可,代码如下:

function flatten(arr) {
    const stack = [arr];
    const result = [];

    while (stack.length > 0) {
        const node = stack.shift();

        if (Array.isArray(node)) {
            stack.unshift(...node);
        } else {
            result.push(node);
        }
    }

    return result;
}

其中的关键点是,我们 while 循环的判断条件是栈的长度

BFS

BFS 的全称是 Broad First Search,也叫广度优先遍历,它没有递归版的模板,只有遍历版的模板,形式为:

function BFS(tree) {
    const visited = new Set();
    const queue = [tree.root];

    while (queue.length) {
        const node = queue.shift();

        if (!visited.has(node)) {
            visited.add(node);

            // process corrent node here
            // ...

            queue.push(...node.children);
        }
    }
}

注意:

  1. 它是用一个队列来储存状态的。
  2. while 循环的判断条件是这个队列的长度

总结

  1. 各种形式的遍历,直接调用模板之后就会发现很容易写了,这些模板最好记在脑子里
  2. 递归转遍历看起来可能很难,但是模板能帮到你。