美文网首页二叉树
二叉树的Morris遍历

二叉树的Morris遍历

作者: 铜炉 | 来源:发表于2021-01-06 21:07 被阅读0次

前言

前面已经进行过二叉树的递归遍历和迭代遍历,在前两种遍历中,二叉树遍历的空间复杂度都是O(n),这是因为不管是递归遍历还是迭代遍历,都用了栈空间来帮我们维护调用关系,方便我们在走到端点的时候可以回退。

那么,换一个思路想想,我们可不可以不用栈,而是通过记录下节点要回退的位置,帮助我们直接退回到目标节点呢?这个回退的指针我们又该怎么维护呢?可以发现,二叉树的叶子节点的左右指针都为空,而我们需要回退的位置也是在叶子节点触发,那么我们考虑可以将叶子节点的指针指向目标节点,这样也就不需要额外的栈空间了。

而事实上,Morris遍历之所以能将二叉树的空间复杂度降为O(1),也是这么做的。

Morris遍历的实现原则

1、如果cur无左孩子,cur向右移动(cur=cur.right)
2、 如果cur有左孩子,找到cur左子树上最右的节点,记为mostright
1)如果mostright的right指针指向空,让其指向cur,cur向左移动(cur=cur.left)
2)如果mostright的right指针指向cur,让其指向空,cur向右移动(cur=cur.right)

以上就是Morris遍历的规则,其中mostright的节点,即为节点在中序遍历中的前序节点。
Morris遍历的本质就是会遍历两次节点,一个快指针,一个慢指针,慢指针到达一个节点的时候,快指针就走到当前节点的左子树的最右节点,将该节点的右指针指向慢指针的位置,然后慢指针再移动到下一步。如此往复,等回退的时候,就可以通过之前便利过程中建立的指针关系迅速回到目标节点位置。

Morris前序遍历
接下来,先看一下Morris前序遍历的实现。

public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) {
            return res;
        }
        // 慢指针
        TreeNode cur = root;
        // 快指针,寻找cur的中序遍历的前序节点
        TreeNode mostright = null;
        
        while (cur != null) {
            // 快指针移动到左子树
            mostright = cur.left;
            
            if (mostright != null) {
                // 左子树不为空,快指针移动到左子树的最右节点,即cur的中序遍历的前序节点
                // 此处注意不能走过头,如果最右节点已经标记过,就停在最右节点处
                while (mostright.right != null && mostright.right != cur) {
                    mostright = mostright.right;
                }
                
                if (mostright.right == null) {
                    // 如果是最右节点还没有和当前节点建立关系,就建立关系,
                    // 因为是前序遍历,cur指针在移动之前都要记录
                    res.add(cur.val);
                    mostright.right = cur;
                    cur = cur.left;
                    continue;
                } else {
                    // 逻辑到这里,说明本次遍历到的最右节点已经和当前节点建立了关系,本次是第二次遍历到cur所在的节点,所以把关系清除.
                    mostright.right = null;
                }
            } else {
                // 此时的mostright在左节点,如果左节点为空,cur不用向左走了,直接记录下来
                res.add(cur.val);
            }
            // 到这里两种情况,左子树为空,且当前节点已经记录,往右走
            // cur指针到达了某个节点的左子树的最右节点,回到那个节点
            cur = cur.right;
        }
        return res;

    }

Morris中序遍历

在前序遍历的基础上,中序遍历需要改动一下记录cur节点的触发时机

public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<Integer>();
            if (root == null) {
                return res;
            }
            // 慢指针
            TreeNode cur = root;
            // 快指针,寻找cur的中序遍历的前序节点
            TreeNode mostright = null;

            while (cur != null) {
                // 快指针移动到左子树
                mostright = cur.left;

                if (mostright != null) {
                    // 左子树不为空,快指针移动到左子树的最右节点,即cur的中序遍历的前序节点
                    // 此处注意不能走过头,如果最右节点已经标记过,就停在最右节点处
                    while (mostright.right != null && mostright.right != cur) {
                        mostright = mostright.right;
                    }

                    if (mostright.right == null) {
                        // 如果是最右节点还没有和当前节点建立关系,就建立关系,
                        mostright.right = cur;
                        cur = cur.left;
                        continue;
                    } else {
                        // 中序遍历,左子树遍历完成,记录当前节点
                        res.add(cur.val);
                        // 逻辑到这里,说明本次遍历到的最右节点已经和当前节点建立了关系,本次是第二次遍历到cur所在的节点,左子树已经遍历完成,所以把关系清除.
                        mostright.right = null;
                    }
                } else {
                    // 此时的mostright在左节点,如果左节点为空,cur不用向左走了,直接记录下来
                    res.add(cur.val);
                }
                // 到这里两种情况,左子树为空,且当前节点已经记录,往右走
                // cur指针到达了某个节点的左子树的最右节点,回到那个节点
                cur = cur.right;
            }
            return res;
    }

相关文章

  • 遍历二叉树

    1、 Morris 遍历 Morris 遍历可以解决二叉树的前序遍历、中序遍历、后序遍历! 1.1、 什么是 Mo...

  • Morris Traversal

    Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间) - AnnieKim - 博客园

  • morris traversal-建好线索再行遍历 2020-0

    1.morris traversal 莫里斯遍历,是在O(n)时间复杂度和O(1)空间复杂度下实现的二叉树遍历,带...

  • Morris遍历

    二叉树前中后序的递归和非递归实现时间复杂度O(N),额外空间复杂度O(h),h是树高度。如果树很棒状那么O(h)接...

  • Morris算法进行二叉树遍历

    Morris算法进行二叉树遍历 二叉树作为计算机中的一个重要数据结构,在很多领域都会涉及到,而提到二叉树,我们首先...

  • 二叉树 morris遍历

    原理:利用二叉树中空闲指针1.当前节点cur无左子树,cur向右移动2.cur如果有左子树,找左子树上最右的节点m...

  • Morris遍历二叉树

    前言 对于二叉树的遍历,递归的前、中、后序遍历可以说是最经典、最简单的,其次,非递归版的就是手动压栈的方式模拟递归...

  • 99. Recover Binary Search Tree 树

    本题要求常量空间解决问题,所以有了是否常量空间内遍历整棵二叉树的方法。即Morris traversal. 1. ...

  • 二叉树的Morris遍历

    前言 前面已经进行过二叉树的递归遍历和迭代遍历,在前两种遍历中,二叉树遍历的空间复杂度都是O(n),这是因为不管是...

  • 99. 恢复二叉搜索树

    使用morris遍历降低时间复杂度

网友评论

    本文标题:二叉树的Morris遍历

    本文链接:https://www.haomeiwen.com/subject/fouooktx.html