返回
苦心 leetcode 94:二叉树中序遍历,别再执迷递归!
前端
2023-10-29 15:55:45
二叉树中序遍历:递归、迭代和 Morris 三剑客
前言
在算法的世界中,leetcode 94 二叉树中序遍历是一个经典问题,考验着我们对树形结构和遍历算法的理解。传统上,我们习惯于使用递归来解决此题,但今天,我们将打破思维定式,深入探索迭代和 Morris 遍历的奥秘,带你领略算法之美的另一面。
递归解法:简单直接
思路:
递归是解决树形结构问题的经典方法。对于中序遍历,我们只需按照左-根-右的顺序依次遍历二叉树的子树即可。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
helper(root, res);
return res;
}
private void helper(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
helper(root.left, res);
res.add(root.val);
helper(root.right, res);
}
迭代解法:巧用栈
思路:
使用栈可以模拟递归的思想。我们只需将当前节点压入栈中,然后弹出栈顶元素并输出其值,再将弹出元素的右子树压入栈中,以此循环往复,直至栈空。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
return res;
}
Morris 遍历:巧妙无需栈
思路:
Morris 遍历是一种巧妙的无栈遍历算法。它的关键在于利用二叉树的右指针,形成一个指向最左子节点的环形结构。然后,我们只需要沿环形结构移动即可实现中序遍历。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
while (root != null) {
TreeNode pre = root.left;
if (pre == null) {
res.add(root.val);
root = root.right;
} else {
while (pre.right != null && pre.right != root) {
pre = pre.right;
}
if (pre.right == null) {
pre.right = root;
root = root.left;
} else {
res.add(root.val);
pre.right = null;
root = root.right;
}
}
}
return res;
}
结语
leetcode 94 二叉树中序遍历看似简单,却蕴藏着算法思想的精髓。通过递归、迭代和 Morris 遍历三种方法的对比,我们不仅加深了对树形结构的理解,更领略到了算法设计的多样性和创造性。
常见问题解答
- 什么是二叉树的中序遍历?
答:中序遍历是一种遍历二叉树的算法,按照左-根-右的顺序输出二叉树中的节点值。
- 为什么使用递归来解决二叉树遍历问题?
答:递归是一种自然而然的方式来解决树形结构问题,因为树形结构本身具有递归的特性。
- 迭代解法比递归解法有什么优势?
答:迭代解法不需要调用函数,因此在内存使用和效率方面更优于递归解法。
- Morris 遍历和迭代解法的区别是什么?
答:迭代解法使用栈来模拟递归,而 Morris 遍历巧妙地利用二叉树的右指针,不需要使用栈。
- 在实际应用中,哪种遍历方法最常用?
答:递归解法简单易懂,在大多数情况下都是一个不错的选择。但如果需要处理大规模数据或在内存有限的环境中,迭代解法或 Morris 遍历可能是更好的选择。