Morris遍历遍历二叉树

遍历二叉树的递归方法使用了函数栈,非递归方法使用了申请的栈,

两者的额外空间都与树的高度有关,所以空间复杂度为O(h),h为二叉树的高度。

可以使用二叉树叶子节点中大量指向null的指针实现空间复杂度O(1)的遍历。

Morris遍历的实质就是避免使用栈结构,让下层到上层有指针,

具体是通过让底层节点指向null的空闲指针指回上层的某个节点,从而完成下层到上层的移动。

先序中序后序主要基于两个主要步骤,然后输出的位置有所不同,以中序遍历为例。

中序遍历:

1、假设当前子树的头节点为h,让h的左子树中最右节点的right指针指向h,

然后h的左子树继续步骤1的处理过程,直到遇到某一个节点没有左子树时记为node,进入步骤2。

2、从node开始通过每个节点的right指针进行移动并以此打印,假设移动到的节点为cur。

对每一个cur节点都判断cur节点的左子树中最右节点是否指向cur。

Ⅰ 如果是,令cur节点的左子树中最右节点的right指针指向null,即恢复树的本来面貌,

然后打印cur,继续通过cur的right指针移动到下一个节点。重复步骤2。

Ⅱ 如果不是,以cur为头的子树重回步骤1执行。

public void morrisIn(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur1 = root;
        TreeNode cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                // 找到cur1左子树的最右节点
                while (cur2.right != null && cur2.right != cur1)
                    cur2 = cur2.right;
                // cur2.right == null 则往上链接
                if (cur2.right == null) {
                    cur2.right = cur1;
                    cur1 = cur1.left;
                    continue;
                }
                // cur2.right == cur1 则取消链接
                else {
                    cur2.right = null;
                }
            }
            System.out.println(cur1.val);
            cur1 = cur1.right;
        }
    }

先序遍历:

和中序遍历相比差别不大,调整了打印顺序。

public void morrisPre(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur1 = root;
        TreeNode cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                while (cur2.right != null && cur2.right != cur1)
                    cur2 = cur2.right;
                if (cur2.right == null) {
                    cur2.right = cur1;
                    System.out.println(cur1.val);
                    cur1 = cur1.left;
                    continue;
                } else {
                    cur2.right = null;
                }
            } else {
                System.out.println(cur1.val);
            }
            cur1 = cur1.right;
        }
    }

后序遍历:

复杂一些,依次逆序打印所有节点的左子树的右边界,打印的时机放在步骤2的条件Ⅰ被触发的时候。

public void morrisPos(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur1 = root;
        TreeNode cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                while (cur2.right != null && cur2.right != cur1)
                    cur2 = cur2.right;
                if (cur2.right == null) {
                    cur2.right = cur1;
                    cur1 = cur1.left;
                    continue;
                } else {
                    cur2.right = null;
                    printEdge(cur1.left);
                }
            }
            cur1 = cur1.right;
        }
        printEdge(root);
    }

    public void printEdge(TreeNode root) {
        TreeNode tail = reverseEdge(root);
        TreeNode cur = tail;
        while (cur != null) {
            System.out.println(cur.val);
            cur = cur.right;
        }
        reverseEdge(tail);
    }

    public TreeNode reverseEdge(TreeNode from) {
        TreeNode pre = null;
        TreeNode next = null;
        while (from != null) {
            next = from.right;
            from.right = pre;
            pre = from;
            from = next;
        }
        return pre;
    }

全部代码含主函数

package tools;

public class Morris {

    public class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int x) {
            this.val = x;
        }
    }

    public void morrisIn(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur1 = root;
        TreeNode cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                // 找到cur1左子树的最右节点
                while (cur2.right != null && cur2.right != cur1)
                    cur2 = cur2.right;
                // cur2.right == null 则往上链接
                if (cur2.right == null) {
                    cur2.right = cur1;
                    cur1 = cur1.left;
                    continue;
                }
                // cur2.right == cur1 则取消链接
                else {
                    cur2.right = null;
                }
            }
            System.out.println(cur1.val);
            cur1 = cur1.right;
        }
    }

    public void morrisPre(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur1 = root;
        TreeNode cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                while (cur2.right != null && cur2.right != cur1)
                    cur2 = cur2.right;
                if (cur2.right == null) {
                    cur2.right = cur1;
                    System.out.println(cur1.val);
                    cur1 = cur1.left;
                    continue;
                } else {
                    cur2.right = null;
                }
            } else {
                System.out.println(cur1.val);
            }
            cur1 = cur1.right;
        }
    }

    public void morrisPos(TreeNode root) {
        if (root == null)
            return;
        TreeNode cur1 = root;
        TreeNode cur2 = null;
        while (cur1 != null) {
            cur2 = cur1.left;
            if (cur2 != null) {
                while (cur2.right != null && cur2.right != cur1)
                    cur2 = cur2.right;
                if (cur2.right == null) {
                    cur2.right = cur1;
                    cur1 = cur1.left;
                    continue;
                } else {
                    cur2.right = null;
                    printEdge(cur1.left);
                }
            }
            cur1 = cur1.right;
        }
        printEdge(root);
    }

    public void printEdge(TreeNode root) {
        TreeNode tail = reverseEdge(root);
        TreeNode cur = tail;
        while (cur != null) {
            System.out.println(cur.val);
            cur = cur.right;
        }
        reverseEdge(tail);
    }

    public TreeNode reverseEdge(TreeNode from) {
        TreeNode pre = null;
        TreeNode next = null;
        while (from != null) {
            next = from.right;
            from.right = pre;
            pre = from;
            from = next;
        }
        return pre;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Morris m = new Morris();
        TreeNode t1 = m.new TreeNode(4);
        TreeNode t2 = m.new TreeNode(2);
        TreeNode t3 = m.new TreeNode(6);
        TreeNode t4 = m.new TreeNode(1);
        TreeNode t5 = m.new TreeNode(3);
        TreeNode t6 = m.new TreeNode(5);
        TreeNode t7 = m.new TreeNode(7);
        t1.left = t2;
        t1.right = t3;
        t2.left = t4;
        t2.right = t5;
        t3.left = t6;
        t3.right = t7;
        m.morrisPre(t1);
        m.morrisIn(t1);
        m.morrisPos(t1);
    }

}

Morris.java

原文地址:https://www.cnblogs.com/wangkaipeng/p/9813856.html

时间: 2024-10-20 04:29:33

Morris遍历遍历二叉树的相关文章

Construct Binary Tree from Inorder and Postorder Traversal ——通过中序、后序遍历得到二叉树

题意:根据二叉树的中序遍历和后序遍历恢复二叉树. 解题思路:看到树首先想到要用递归来解题.以这道题为例:如果一颗二叉树为{1,2,3,4,5,6,7},则中序遍历为{4,2,5,1,6,3,7},后序遍历为{4,5,2,6,7,3,1},我们可以反推回去.由于后序遍历的最后一个节点就是树的根.也就是root=1,然后我们在中序遍历中搜索1,可以看到中序遍历的第四个数是1,也就是root.根据中序遍历的定义,1左边的数{4,2,5}就是左子树的中序遍历,1右边的数{6,3,7}就是右子树的中序遍历

[二叉树建树]1119. Pre- and Post-order Traversals (30) (前序和后序遍历建立二叉树)

1119. Pre- and Post-order Traversals (30) Suppose that all the keys in a binary tree are distinct positive integers. A unique binary tree can be determined by a given pair of postorder and inorder traversal sequences, or preorder and inorder traversa

先序遍历和后序遍历构建二叉树

递归的方法利用先序遍历和中序遍历构建二叉树,同样也可以利用到中序遍历和后序遍历构建二叉树. //利用先序遍历和中序遍历构建二叉树 TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { TreeNode *root=NULL; if(preorder.size()==0||inorder.size()==0||preorder.size()!=inorder.size()) return

已知二叉树的前序遍历、中序遍历或者中序遍历、后序遍历求二叉树结构的算法

二叉树中的前序遍历是先访问根结点,再访问左子树,右子树. 中序遍历是先访问左子树,再是根结点,最后是右子树. 后序遍历是先访问左子树,再是右子树,最后是根结点. 算法思路是先根据前序遍历的第一个结点或者后序遍历的最后一个结点,查找对应在中序遍历中的位置,就可以确定左子树包含的元素和右子树包含的元素,最后通过递归来实现就可以了. 二叉树的表示形式为 //二叉树的结构表示为 class TreeNode { int val; TreeNode left; TreeNode right; TreeNo

先序遍历创建二叉树,对二叉树统计叶子节点个数和统计深度(创建二叉树时#代表空树,序列不能有误)

#include "stdio.h" #include "string.h" #include "malloc.h" #define NULL 0 #define MAXSIZE 30 typedef struct BiTNode      //定义二叉树数据结构 { char data; struct BiTNode *lchild,*rchild; } BiTNode; void preCreate(BiTNode *& T)   /

一步两步学算法之中序遍历线索二叉树

1 typedef enum 2 { 3 SubTree, //子树 4 Thread //线索 5 }NodeFlag; 6 7 typedef struct ThreadTree 8 { 9 DATA data; 10 NodeFlag lflag; 11 NodeFlag rflag; 12 struct ThreadTree *left; 13 struct ThreadTree *right; 14 }ThreadBinTree; 15 16 ThreadBinTree *Previo

前序遍历和中序遍历重建二叉树

对于二叉树,在此我不做过多讲解,如有不懂,请参照一下链接点击打开链接 1.在此二叉树的定义: struct BinaryTreeNode     {         BinaryTreeNode<T> *_Left;         BinaryTreeNode<T> *_Right;         T _data;     public:         BinaryTreeNode(const T& x)             :_Left(NULL)       

[LeetCode] Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树

Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that duplicates do not exist in the tree. 这道题要求用先序和中序遍历来建立二叉树,跟之前那道Construct Binary Tree from Inorder and Postorder Traversal 由中序和后序遍历建立二叉树原理基本相同,针对这道题,由于先

根据先序遍历中序遍历重建二叉树

根据先序遍历和中序遍历的特点,我们想到了采用递归的方法来实现. 思路:1) 代码的容错性检查,比如:先序遍历和中序遍历长度应相等 2) 先保存先序遍历的第一个点,这个点为结点,接下来循环中序遍历,直到midOrd[index]=该结点,那么接下来就可以采用递归,分别对结点左边和右边的序列采用相同的方法. 3) 直到中序遍历中的序列中只含一个点,整个过程结束. 下面是我写的代码,一直显示错误,不知道什么原因. // 先序中序重建二叉树.cpp : Defines the entry point f

二叉树 二叉树的性质 存储结构 遍历二叉树 C实现二叉树的创建和遍历 线索二叉树

定义 二叉树(binary tree)是n(n>=0)个结点的有限集合,该集合为空集合称为空二叉树,或者有一个根结点和两棵互不相交的,分别称为树根结点的左孩子树和右孩子树组成. 二叉树的特点 每个结点最多有两棵子树,所以二叉树总没有度大于2的结点 左子树和右子树是有顺序的,次数不能任意颠倒 即使树中某结点只有一棵子树,也要区分是左子树还是右子树 特殊的二叉树 1. 斜树 所有的结点都只有左子树的二叉树称为左斜树; 所有的结点都只有右子树的二叉树称为右斜树; 这两者统称为斜树 2. 满二叉树 在一