前端算法面试难点解析:二叉树遍历的四种经典方式
二叉树是前端面试中经常遇到的算法考点,掌握其遍历方式是必备技能。本文将深入解析二叉树的前序、中序、后序和层序遍历,帮助前端开发者攻克这一面试难点。
二叉树遍历基础概念
二叉树遍历是指按照某种顺序访问树中的所有节点,确保每个节点都被访问且仅被访问一次。根据访问顺序的不同,主要分为四种遍历方式:
- 前序遍历:根节点 → 左子树 → 右子树
- 中序遍历:左子树 → 根节点 → 右子树
- 后序遍历:左子树 → 右子树 → 根节点
- 层序遍历:从上到下、从左到右逐层访问
前序遍历详解
算法原理
前序遍历遵循"根左右"的顺序:
- 首先访问根节点
- 然后递归遍历左子树
- 最后递归遍历右子树
递归实现
function preorderTraversal(root) {
let result = [];
function traverse(node) {
if (!node) return;
result.push(node.val); // 访问根节点
traverse(node.left); // 遍历左子树
traverse(node.right); // 遍历右子树
}
traverse(root);
return result;
}
迭代实现(栈模拟)
function preorderTraversal(root) {
if (!root) return [];
let stack = [root];
let result = [];
while (stack.length) {
const node = stack.pop();
result.push(node.val);
// 注意:先压右节点,再压左节点
if (node.right) stack.push(node.right);
if (node.left) stack.push(node.left);
}
return result;
}
中序遍历详解
算法原理
中序遍历遵循"左根右"的顺序:
- 先递归遍历左子树
- 然后访问根节点
- 最后递归遍历右子树
递归实现
function inorderTraversal(root) {
let result = [];
function traverse(node) {
if (!node) return;
traverse(node.left); // 遍历左子树
result.push(node.val); // 访问根节点
traverse(node.right); // 遍历右子树
}
traverse(root);
return result;
}
迭代实现
function inorderTraversal(root) {
let stack = [];
let result = [];
let curr = root;
while (curr || stack.length) {
while (curr) {
stack.push(curr);
curr = curr.left; // 一直向左走到底
}
curr = stack.pop();
result.push(curr.val); // 访问节点
curr = curr.right; // 转向右子树
}
return result;
}
后序遍历详解
算法原理
后序遍历遵循"左右根"的顺序:
- 先递归遍历左子树
- 然后递归遍历右子树
- 最后访问根节点
递归实现
function postorderTraversal(root) {
let result = [];
function traverse(node) {
if (!node) return;
traverse(node.left); // 遍历左子树
traverse(node.right); // 遍历右子树
result.push(node.val); // 访问根节点
}
traverse(root);
return result;
}
迭代实现
function postorderTraversal(root) {
if (!root) return [];
let stack = [root];
let result = [];
while (stack.length) {
const node = stack.pop();
result.unshift(node.val); // 从前面插入,实现逆序
if (node.left) stack.push(node.left);
if (node.right) stack.push(node.right);
}
return result;
}
层序遍历详解
算法原理
层序遍历又称广度优先遍历(BFS),按照树的层级从上到下、从左到右依次访问每个节点。通常使用队列来实现。
实现代码
function levelOrder(root) {
if (!root) return [];
let queue = [root];
let result = [];
while (queue.length) {
let levelSize = queue.length;
let currentLevel = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
currentLevel.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(currentLevel);
}
return result;
}
遍历方式对比与应用场景
| 遍历方式 | 顺序 | 典型应用场景 | |---------|------|------------| | 前序遍历 | 根左右 | 复制二叉树、表达式树的前缀表示 | | 中序遍历 | 左根右 | 二叉搜索树的有序输出 | | 后序遍历 | 左右根 | 删除二叉树、表达式树的后缀表示 | | 层序遍历 | 按层 | 计算二叉树深度、寻找最短路径 |
面试常见问题
- 递归与迭代的选择:递归代码简洁但可能有栈溢出风险;迭代更可控但代码复杂
- 时间复杂度:所有遍历方式都是O(n),n为节点数
- 空间复杂度:最坏情况下都是O(n),平均情况下递归为O(h),h为树高
掌握这四种遍历方式不仅能帮助通过面试,更是理解树结构操作的基础。建议读者动手实现每种遍历,并尝试解决相关变种题目,如锯齿形层序遍历、N叉树遍历等。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考