美文网首页
二叉树的遍历(二)二叉树的进阶遍历方式

二叉树的遍历(二)二叉树的进阶遍历方式

作者: chengcongyue | 来源:发表于2019-04-11 00:01 被阅读0次

    引言

    无论是递归遍历还是非递归遍历,我们都无法将空间复杂度达到O(1),因为对于之前的遍历来说,它们都是使用栈的(递归的是函数栈,而非递归的自己创建的栈),如果我们不使用栈能否实现遍历那,当然是可以的,这时候就要借助我们的叶子结点的空指针.这种遍历方式叫做Morris遍历.
    Morris遍历就是借助指针的改变,从而实现能够从下层指向上层.
    我来大概描述一下整个的过程,首先我们以中序遍历为例子,这是两个变量一个是cur1,另一个是cur2,循环的条件cur1!=null,cur2表示的是cur2的左子树,然后以cur2位基准,找到cur2中最右的结点,让这个结点,连向cur1.


    图片.png

    ,然后我们一次执行这样的操作,使得最后的样子如下


    图片.png
    当我们走到cur2为空的时候,即这个时候我们走到了整个数的最左边,我们就可以开始遍历了,依次的通过right的指针运动.
    在运动的过程中,但我们到达了原来的cur1,我们就要把指向变成null,我们来看一下
    图片.png

    然后我们来写一下大概的代码

    Node cur1=head;
    Node cur2=null;
    while(cur1!=null)
    {
         cur2=cur1.left;
         if(cur2!=null)//这个步骤是比不可少的
          {
                //循环,连接    
                //断开连接
          }
          cur1一直向right移动,然后打印
    }
    

    大概的思路就是上面那样,但是我们如果将连接和断开连接在同一个if中执行那,我们来看一下最终的代码

    public static void morrisInOrder(Node head)
        {
            if(head!=null)
            {
                Node cur1=head;
                Node 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;
                        }
                    }
                    System.out.print(cur1.value+" ");
                    cur1=cur1.right;
                }
            }
        }
    

    上面演示的是中序遍历,那么先序遍历和后序遍历我们要怎么样完成那,相对以之前的非递归的代码,我们这里的差异并不是很大,只需要稍微的修改一下打印的时机就可以了


    图片.png
    图片.png

    所有综上所述,我们打印的时机为

    • 建立连接时
    • 当cur2为空时
      于是我们的代码也就出来了
    public static void morrisPreOrder(Node head)
        {
            if(head!=null)
            {
                Node cur1=head;
                Node 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.print(cur1.value+" ");
                            cur1=cur1.left;
                            continue;
                        }else
                        {
                            cur2.right=null;
                        }
                    }else
                    {
                        System.out.println(cur1.value+" ");
                    }
                    cur1=cur1.right;
                }
            }
        }
    

    后序遍历打印的顺序如图


    图片.png

    所以这个时候打印的时机,就是在断开建立的连接的时候
    而且需要时逆序

    public static void printEdge(Node head)
        {
            Node tail=reverseEdge(head);
            Node cur=tail;
            while(cur!=null)
            {
                System.out.print(cur.value+" ");
                cur=cur.right;
            }
            reverseEdge(tail);
        }
        
        //相当于是链表的逆序
        public static Node reverseEdge(Node from)
        {
            Node pre=null;
            Node next=null;
            while(from!=null)
            {
                next=from.right;
                from.right=pre;
                pre=from;
                from=next;
            }
            return pre;
        }
    

    然后在断开的时候打印
    即printEdge(cur1.left);
    不要忘了跳出循环时,要打印head开头的那个"链表"
    代码如下

    public static void morrisPos(Node head) {
            if (head == null) {
                return;
            }
            Node cur1 = head;
            Node 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(head);
            System.out.println();
        }
    

    总结

    这就是我们的进阶的遍历方式,空间复杂度为O(1),注意三种遍历方式的区别,就是打印的时机,我们需要注意一下后序的遍历,因为这里用到了链表的逆置

    相关文章

      网友评论

          本文标题:二叉树的遍历(二)二叉树的进阶遍历方式

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