只需消耗O(1)空间遍历二叉树

  背景:在《数据结构》中,二叉树是有两个左右结点的,并且没有父结点,因此我们在使用递归遍历、非递归遍历都需要消耗O(logN)的栈空间,只是使用递归时消耗的是方法栈罢了,而消耗这一部分只是为了记住父节点,以达到我们的目的。

  但是有没有存在一种方式可以利用树本身来记住,这就是要介绍的morris的遍历。利用的是二叉树的叶子结点游离的指针。

假设这样的步骤:

1、假设当前子树的头节点为h,让h的左子树中最右节点的right指针指向h,然后h的左子树继续步骤1的处理过程,直到遇到某一个节点记为node,开始进入步骤2

2、从node开始通过每个节点的right指针进行移动,并依次打印,假设移动到节点为cur。对每一个cur节点的左子树中最右节点是否指向cur

(1)如果是,让cur节点的左子树中最右节点的right指针指向空,也就是把步骤1的调整后再逐渐调整回来,然后打印cur

(2)如果不是,以cur为头的子树重回步骤1执行

 

假设你可以想象吧,简单的说就是连线后把线删除,遇到新的右子树就再继续这样做,连线删线...

 

但是呢?

 

我写的并不顺利,因为被重复调用陷入进去了,都在考虑怎么在移动中使用连线删线,完全没法写下去了。。。因为这是最直观的思路,但是已经陷入了局部了

翻看了左程云的《程序员面试经典》,简直觉得大神就是牛,我真的完全没有想到,他采用了循环,然后对于当前的节点和处理操作进行控制,简直简单容易理解

public void morrisIn(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;  
                }
            }
            System.out.print(cur1.val+" ");
            cur1=cur1.right;  //更换当前的结点
        }
        System.out.println();
}

   至少我写不出这样的,太难想了,有没有大佬有更好地想法或者是思路,一起交流

posted @ 2017-11-15 19:03  还想着呢  阅读(120)  评论(0)    收藏  举报