二叉树神级遍历方法--Morris遍历

Morris遍历

morris遍历可以将额外空间复杂度降到O(1),而常规的递归或者非递归遍历额外空间复杂度都是O(h)。常规的递归或者非递归遍历其实也用到了栈结构,在处理完子节点之后可以回到父节点,然而对于二叉树这种结构,都是从父节点指向子节点,所以子节点指向父节点通常使用栈结构来实现。

​ morris遍历避免使用栈结构来实现子节点到父节点,而是利用二叉树最下面一层节点指向null节点的指针指向上面某一个节点。比如二叉树最下面一层某个节点没有右孩子,那么该节点的right指针就指向空,这些指针被叫做空闲指针,morris正是利用这些空闲指针。

具体遍历过程

假设当前节点为 cur,一开始cur来到树的根节点,开始移动cur:

  1. 如果cur为null,则停止移动;
  2. 如果cur没有左子树,则让cur向右移动,cur = cur.right;
  3. 如果cur有左子树,则找左子树的最右边节点,记为mostRight;
    • 如果 mostRight 的 right 指针指向 null,让 right 指针指向当前节点(mostRight.right = cur),然后让 cur 向左移动(cur = cur.left);
    • 如果 mostRight 的 right 指针指向 cur,让right 指针指向 null(mostRight.right = null),然后让 cur 向右移动( cur = cur.right)。

举个例子

在这里插入图片描述

1、cur一开始来到4,4有左子树,找左子树的最右边节点,找到3,因为3.right = null,所以让3的right指向cur,也就是3,然后cur向左移动,来到2;

在这里插入图片描述

2、cur来到2,2有左子树,找左子树的最右边节点,找到1,因为1.right = null,所以让1的right指向cur,也就是2,然后cur向左移动,来到1;

在这里插入图片描述

3、cur来到1,1没有左子树,所以向右移动,来到2;

4、cur来到2,2有左子树,找左子树的最右边节点,找到1,因为1.right = cur,所以让1.right = null,cur向右移动,来到3;

5、cur来到3,3没有左子树,所以向右移动,来到4;

6、cur来到4,4有左子树,找左子树的最右边节点,找到3,因为3.right = cur,所以让3.right = null,cur向右移动,来到6;

7、cur来到6,6有左子树,找左子树的最右边节点,找到5,因为5.right = null,所以让5.right = cur,也就是6,cur向左移动,来到5;

在这里插入图片描述

8、cur来到5,5没有左子树,所以向右移动,来到6;

9、cur来到6,6有左子树,找左子树的最右边节点,找到5,因为5.right = cur,所以让5.right = null,cur向右移动,来到7;

10、cur来到7,7没有左子树,所以向右移动,来到null;

11、因为cur为null,所以停止移动。

​ 从以上遍历过程可以看出,cur依次到达的节点分别是:4、2、1、2、3、4、6、5、6、7,我们将这个序列叫 Morris 序。从到达的这些节点中可以看出,有子节点的节点4,2,6分别来到了两次,第一次来到是在左子树的最右节点指向空的时候,第二次来到是在左子树最右边节点指向自己的时候,这也是 Morris 遍历和 Morris 序的实质。

具体代码

public void morris(TreeNode node){
        if(node == null){
            return ;
        }
        TreeNode cur = null;
        TreeNode mostRight = null;
        while (cur != null){
            mostRight = cur.left;
            //有左子树的情况
            if(mostRight!=null){
                //一直往下找,找到左子树最右边的节点
                while(mostRight.right != null &&mostRight.right != cur){
                    mostRight = mostRight.right;
                }
                //出了上面的while循环,此时mostRight已经是左子树最右边的节点了
                //判断mostRight是指向自己的还是指向空的,若指向空,则让它指向自己,并左移
                // 否则就是指向自己,则让它指向空
                if(mostRight.right == null){
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                }else {
                    mostRight.right = null;
                }
            }
            //没有左子树的情况
            cur = cur.right;
        }
    }

根据Morris遍历序列,可以改造先序、中序和后序。

先序遍历代码如下

public static void morrisPre(TreeNode node){
        if(node == null){
            return ;
        }
        TreeNode cur = node;
        TreeNode mostRight = null;
        while (cur != null){
            mostRight = cur.left;
            //有左子树的情况
            if(mostRight!=null){
                //一直往下找,找到左子树最右边的节点
                while(mostRight.right != null &&mostRight.right != cur){
                    mostRight = mostRight.right;
                }
                //出了上面的while循环,此时mostRight已经是左子树最右边的节点了
                //判断mostRight是指向自己的还是指向空的,若指向空,则让它指向自己,并左移
                // 否则就是指向自己,则让它指向空
                if(mostRight.right == null){
                    mostRight.right = cur;
                    System.out.print(cur.value + " ");
                    cur = cur.left;
                    //回到最外层的 while,继续判断 cur 的情况
                    continue;
                }else {
                    mostRight.right = null;

                }
            }else {
                System.out.print(cur.value + " ");
            }
            //没有左子树的情况
            cur = cur.right;
        }
    }

    public static void main(String[] args) {
        TreeNode node = new TreeNode(4);
        node.left = new TreeNode(2);
        node.right = new TreeNode(6);
        node.left.left = new TreeNode(1);
        node.left.right = new TreeNode(3);
        node.right.right = new TreeNode(7);
        node.right.left = new TreeNode(5);
        morrisPre(node);
    }

测试结果

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值