二叉树遍历进阶:Morris 遍历的原理与 Java 实现(空间复杂度 O (1))

Morris遍历原理与Java实现

二叉树遍历进阶:Morris 遍历的原理与 Java 实现(空间复杂度 $O(1)$)

二叉树遍历是计算机科学中的基础算法,常见方法如递归前序、中序和后序遍历,空间复杂度通常为 $O(n)$($n$ 为节点数),因为需要栈或递归调用栈。然而,在某些资源受限场景中,高空间开销可能成为瓶颈。Morris 遍历算法通过巧妙修改树结构,在常数空间内完成遍历,空间复杂度为 $O(1)$。本文将详细解释其原理,并提供完整的 Java 实现代码。

Morris 遍历原理

Morris 遍历的核心思想是“线索化”:在不使用额外栈或递归的前提下,通过临时修改节点的右指针(称为“线索”),实现遍历过程。遍历结束后,树结构完全恢复原状。算法以中序遍历为例(其他遍历类似),步骤如下:

  1. 初始化:从根节点开始,当前节点设为根。
  2. 遍历循环
    • 如果当前节点无左子树:
      • 访问当前节点(输出其值)。
      • 移动到右子树(当前节点设为右子节点)。
    • 如果当前节点有左子树:
      • 找到当前节点左子树的最右节点(称为“前驱节点”)。
      • 如果前驱节点的右指针为空:
        • 将前驱节点的右指针指向当前节点(创建线索)。
        • 移动到左子树(当前节点设为左子节点)。
      • 如果前驱节点的右指针指向当前节点(表示已访问):
        • 将前驱节点的右指针置空(恢复树结构)。
        • 访问当前节点。
        • 移动到右子树。
  3. 终止条件:当当前节点为空时,遍历结束。

该过程确保每个节点被访问一次,且空间复杂度为 $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
    }
}

分析与讨论
  • 空间复杂度:仅使用固定数量的指针变量(如 currentpredecessor),空间复杂度为 $O(1)$。
  • 时间复杂度:每个节点被访问两次,总操作次数为 $2n$,时间复杂度为 $O(n)$。
  • 适用场景:Morris 遍历适用于内存敏感环境,如嵌入式系统或大数据处理,但需注意:临时修改指针可能影响并发操作,建议在单线程中使用。
  • 扩展性:类似原理可应用于前序和后序遍历,只需调整访问节点的时机。
结论

Morris 遍历算法通过线索化技术,在常数空间内完成二叉树遍历,解决了传统方法的高空间开销问题。本文的 Java 实现展示了其简洁性和实用性,适用于资源优化场景。开发者可根据需求调整代码,应用于更复杂的数据结构处理中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值