一道面试题引发的思考
概述
最近复习算法,正好想到了之前的一道面试题,由此引发了许多思考。在此记录一下,供以后开发时参考,希望对其他人也有用。
一道面试题
面试题如下:
// 要求实现一个 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
DFS 的全称是 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);
}
}
}
注意:
- 递归版在递归函数里面储存了状态,而遍历版使用了一个栈来储存状态。
- 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);
}
}
}
注意:
- 它是用一个队列来储存状态的。
- while 循环的判断条件是这个队列的长度。
总结
- 各种形式的遍历,直接调用模板之后就会发现很容易写了,这些模板最好记在脑子里。
- 递归转遍历看起来可能很难,但是模板能帮到你。