【LeetCode】Morris解法求二叉树的前中后序遍历(带图解)

目录

Morris的通用过程:

通用图解:

 通用代码展示:

前序遍历

代码展示:

中序遍历

代码展示:

后序遍历:

后序图解:

 代码展示:


Morris 遍历使用二叉树节点中大量指向 null 的指针,由 Joseph Morris 于 1979 年发明。 时间复杂度:O(n) 额外空间复杂度:O(1)

在解题之前先看一下

Morris的通用过程:

Morris的整体思路就是将,以某个根节点开始,找到它左子树的最右侧结点之后与当前根节点进行连接,连接之后cur指针可以完整地顺着节点遍历完整个子树,直至为空。

通用图解:

【LeetCode】Morris解法求二叉树的前中后序遍历(带图解)_第1张图片

 通用代码展示:

public void pre(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur1 = root;//当前遍历到的节点
        TreeNode cur2 = null;//记录当前节点的左子树
        //当前节点非空进循环
        while(cur1 != null){
            //当前节点的左子树
            cur2 = cur1.left;
            //cur2非空,否则已走到了最左侧
            if(cur2 != null){
                //找最右侧的节点
                //不可为空,也不能指向cur1
                while(cur2.right!= null && cur2.right != cur1){
                    cur2 = cur2.right;
                }
                //出循环有两种可能
                //1.cur2.right为空,此时建立起链接
                if(cur2.right == null){
                    cur2.right = cur1;
                    cur1 = cur1.left;
                }else{
                    //此时指向cur1说明已经遍历过,断开连接
                    cur2.right = null;
                    cur1 = cur1.right;
                }

            }else{
                //走到了最左侧,使得cur1往右边走
                cur1 = cur1.right;
            }
        }
    }

前序遍历

  1. 在某个根结点创建连线的时候打印。因为我们是顺着左边的根节点来创建连线,且创建的过程只有一次。

  2. 打印某些自身无法创建连线的节点,也就是叶子节点

代码展示:

     public void pre(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur1 = root;//当前遍历到的节点
        TreeNode cur2 = null;//记录当前节点的左子树
        //当前节点非空进循环
        while(cur1 != null){
            //当前节点的左子树
            cur2 = cur1.left;
            //cur2非空,否则已走到了最左侧
            if(cur2 != null){
                //找最右侧的节点
                //不可为空,也不能指向cur1
                while(cur2.right!= null && cur2.right != cur1){
                    cur2 = cur2.right;
                }
                //出循环有两种可能
                //1.cur2.right为空,此时建立起链接
                if(cur2.right == null){
                    cur2.right = cur1;
                    System.out.println(cur1.val + " ");
                    cur1 = cur1.left;
                }else{
                    //此时指向cur1说明已经遍历过,断开连接
                    cur2.right = null;
                    cur1 = cur1.right;
                }

            }else{
                //走到了最左侧,使得cur1往右边走
                System.out.println(cur1.val + " ");
                cur1 = cur1.right;
            }
        }
    }

中序遍历

从最左侧开始顺着右节点打印。也就是在将 cur1 切换到上层节点的时候。

代码展示:

    public void pre(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur1 = root;//当前遍历到的节点
        TreeNode cur2 = null;//记录当前节点的左子树
        //当前节点非空进循环
        while(cur1 != null){
            //当前节点的左子树
            cur2 = cur1.left;
            //cur2非空,否则已走到了最左侧
            if(cur2 != null){
                //找最右侧的节点
                //不可为空,也不能指向cur1
                while(cur2.right!= null && cur2.right != cur1){
                    cur2 = cur2.right;
                }
                //出循环有两种可能
                //1.cur2.right为空,此时建立起链接
                if(cur2.right == null){
                    cur2.right = cur1;
                    cur1 = cur1.left;
                }else{
                    //此时指向cur1说明已经遍历过,断开连接
                    cur2.right = null;
                    System.out.println(cur1.val + " ");
                    cur1 = cur1.right;
                }

            }else{
                //走到了最左侧,使得cur1往右边走
                System.out.println(cur1.val + " ");
                cur1 = cur1.right;
            }
        }
    }

后序遍历:

后序遍历相较于前序与中序比较复杂

当我们遍历到最左侧,左边的连线已经创建完毕了,

我们需要打印4 打印5,2 打印 6,最后打印7 3 1;

我们将一个节点的连续右节点当成一个单链表来看待

当我们返回上层之后,也就是将连线断开的时候,打印下层的单链表的逆序

后序图解:

【LeetCode】Morris解法求二叉树的前中后序遍历(带图解)_第2张图片

 代码展示:

    public void print(TreeNode root){
        TreeNode x = reverse1(root);
        while(x != null){
            System.out.println(x.val);
            x = x.right;
        }
    }

    public TreeNode reverse1(TreeNode root){
        //反转链表,1.头插法2.迭代3.递归
        TreeNode head = null;
        while(root != null){
            head = addFirst(head,root.val);
            root = root.right;
        }
        return head;
    }

    public TreeNode addFirst(TreeNode head, char val){
        TreeNode x = new TreeNode(val);
        if(head == null){
            head = x;
        }else {
            x.right = head;
            head = x;
        }
        return head;
    }
    public void post(TreeNode root){
        if(root == null){
            return;
        }
        TreeNode cur1 = root;//当前遍历到的节点
        TreeNode cur2 = null;//记录当前节点的左子树
        //当前节点非空进循环
        while(cur1 != null){
            //当前节点的左子树
            cur2 = cur1.left;
            //cur2非空,否则已走到了最左侧
            if(cur2 != null){
                //找最右侧的节点
                //不可为空,也不能指向cur1
                while(cur2.right!= null && cur2.right != cur1){
                    cur2 = cur2.right;
                }
                //出循环有两种可能
                //1.cur2.right为空,此时建立起链接
                if(cur2.right == null){
                    cur2.right = cur1;
                    cur1 = cur1.left;
                }else{
                    //此时指向cur1说明已经遍历过,断开连接
                    cur2.right = null;
                    //打印cur1.left链表的反转
                    print(cur1.left);
                    cur1 = cur1.right;
                }

            }else{
                //走到了最左侧,使得cur1往右边走
                cur1 = cur1.right;
            }
        }
        //最后打印根节点的反转
        print(root);
    }

你可能感兴趣的:(Java练习题,二叉树,链表,leetcode,算法,java)