二叉树遍历进阶:Morris 遍历的原理与 Java 实现(空间复杂度 $O(1)$)
二叉树遍历是计算机科学中的基础算法,常见方法如递归前序、中序和后序遍历,空间复杂度通常为 $O(n)$($n$ 为节点数),因为需要栈或递归调用栈。然而,在某些资源受限场景中,高空间开销可能成为瓶颈。Morris 遍历算法通过巧妙修改树结构,在常数空间内完成遍历,空间复杂度为 $O(1)$。本文将详细解释其原理,并提供完整的 Java 实现代码。
Morris 遍历原理
Morris 遍历的核心思想是“线索化”:在不使用额外栈或递归的前提下,通过临时修改节点的右指针(称为“线索”),实现遍历过程。遍历结束后,树结构完全恢复原状。算法以中序遍历为例(其他遍历类似),步骤如下:
- 初始化:从根节点开始,当前节点设为根。
- 遍历循环:
- 如果当前节点无左子树:
- 访问当前节点(输出其值)。
- 移动到右子树(当前节点设为右子节点)。
- 如果当前节点有左子树:
- 找到当前节点左子树的最右节点(称为“前驱节点”)。
- 如果前驱节点的右指针为空:
- 将前驱节点的右指针指向当前节点(创建线索)。
- 移动到左子树(当前节点设为左子节点)。
- 如果前驱节点的右指针指向当前节点(表示已访问):
- 将前驱节点的右指针置空(恢复树结构)。
- 访问当前节点。
- 移动到右子树。
- 如果当前节点无左子树:
- 终止条件:当当前节点为空时,遍历结束。
该过程确保每个节点被访问一次,且空间复杂度为 $O(1)$,因为只使用常数个指针变量。时间复杂度为 $O(n)$,因为每个节点被访问两次(一次创建线索,一次访问)。数学上,遍历路径长度总和为 $2n$,故时间复杂度为 $O(n)$。
Java 实现
以下是用 Java 实现的 Morris 中序遍历代码。代码包括二叉树节点定义和遍历函数,注释详细解释每一步逻辑。注意:树结构在遍历后自动恢复。
// 定义二叉树节点类
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class MorrisTraversal {
// Morris 中序遍历函数
public void inorderTraversal(TreeNode root) {
TreeNode current = root; // 当前节点
while (current != null) {
if (current.left == null) {
// 无左子树:访问当前节点并移动到右子树
System.out.print(current.val + " ");
current = current.right;
} else {
// 有左子树:找到前驱节点
TreeNode predecessor = current.left;
while (predecessor.right != null && predecessor.right != current) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
// 前驱节点右指针为空:创建线索并移动到左子树
predecessor.right = current;
current = current.left;
} else {
// 前驱节点右指针指向当前节点:恢复树结构、访问节点、移动到右子树
predecessor.right = null;
System.out.print(current.val + " ");
current = current.right;
}
}
}
}
// 示例用法
public static void main(String[] args) {
// 构建示例二叉树: 根(1), 左(2), 右(3)
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
// 执行 Morris 遍历
MorrisTraversal traversal = new MorrisTraversal();
System.out.println("中序遍历结果:");
traversal.inorderTraversal(root); // 输出: 2 1 3
}
}
分析与讨论
- 空间复杂度:仅使用固定数量的指针变量(如
current和predecessor),空间复杂度为 $O(1)$。 - 时间复杂度:每个节点被访问两次,总操作次数为 $2n$,时间复杂度为 $O(n)$。
- 适用场景:Morris 遍历适用于内存敏感环境,如嵌入式系统或大数据处理,但需注意:临时修改指针可能影响并发操作,建议在单线程中使用。
- 扩展性:类似原理可应用于前序和后序遍历,只需调整访问节点的时机。
结论
Morris 遍历算法通过线索化技术,在常数空间内完成二叉树遍历,解决了传统方法的高空间开销问题。本文的 Java 实现展示了其简洁性和实用性,适用于资源优化场景。开发者可根据需求调整代码,应用于更复杂的数据结构处理中。
Morris遍历原理与Java实现
1085

被折叠的 条评论
为什么被折叠?



