经典算法与数据结构(5)—— 二叉树的Moris遍历 (空间复杂度O(1))

二叉树的遍历(无论是递归还是非递归),都会用到栈结构,其中递归版本用的是系统栈,非递归版本用的是Java自带的Stack结构,空间复杂度均为递归栈的深度,即O(h),h为二叉树的高度。此外,层次遍历的空间复杂度是O(w),w为二叉树的宽度。
Moris遍历的神奇之处在于,他的空间复杂度只有O(1),核心思想是利用叶子结点的空指针,下面介绍主要流程和代码实现。

Moris遍历的流程

Moris遍历本身是没有前序、中序、后序的概念的,但是利用Moris遍历可以实现这三种形式的遍历。先来介绍Moris遍历本身的流程(给定根节点root):
首先,如果root为null,直接返回。否则令node=root,进行循环:

WHILE (node != null)

1.如果node没有左子树,node=node.right;
2.如果node有左子树,获取左子树的最右结点right_most(right!= null && right!=node),如果最右结点的right为null见3, 否则(right为node)见4 。
3. right_most.right == null : 令right_most的right指向node,node = node.left
4. right_most.right == node : 令right_most的right指向null, node= node.right

END WHILE

Moris遍历流程的含义

第一次看到这个流程可能会有点懵,实际上Moris遍历就是利用叶结点的指针进行标记,如果左子树还没遍历过,此时左子树的最右结点的right指针便指向null,这个时候便应该去遍历左子树;如果某结点的左子树遍历过了,再次来到了该节点,就会发现此时左子树的最右结点的right指向该节点,这个时候就可以去遍历右子树了,因为左子树已经完成遍历了。

就Moris遍历而言,所有的子树的遍历顺序(也就是node引用的顺序)都是:根节点 --> 左子树 --> 根节点 --> 右子树。 也就是说所有左孩子不为空的结点都会被遍历两次,若左孩子为空就只遍历一次。

那么如何利用Moris遍历进行前序、中序、后序遍历呢?从刚才的顺序 ”根节点1 --> 左子树 --> 根节点2 --> 右子树“ (1和2用来区分第一次和第二次遍历根节点)可以看出:

  • 如果忽略两次重复遍历中的第一次,顺序就变成了 左子树 --> 根节点2 --> 右子树 , 也就是中序遍历。
  • 如果忽略第二次,就变成了根节点1 --> 左子树 --> 右子树,也就是先序遍历。
  • 至于后序遍历,不能以相似的方法直接得到,待会会介绍。

代码实现

Moris遍历


public static void moris(TreeNode head){
   
        if (head == null)
            return;
        TreeNode curr = head;
        while (curr != null){
   

            if (curr.left != null){
   
                TreeNode right_most = curr.left;
                while (right_most.right != null && right_most.right!=curr)
                    right_most = right_most.right;
                if(right_most.right == null){
   
                    // 第一次来到这个结点
                    right_most.right = curr;
                    curr = curr.left;
                } else{
   
                    // 左边已经遍历完了,第二次来到这个结点
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值