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

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

作者: 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),注意三种遍历方式的区别,就是打印的时机,我们需要注意一下后序的遍历,因为这里用到了链表的逆置

相关文章

  • 二叉树的遍历

    二叉树的遍历 二叉树常用的遍历方式有:前序遍历、中序遍历、后序遍历、层序遍历四种遍历方式,不同的遍历算法,其思想略...

  • 数据结构与算法之二叉树遍历(七)

    目录 前序遍历中序遍历后序遍历层序遍历遍历方式的选择条件根据遍历结果重构二叉树翻转二叉树计算二叉树的高度判断一棵树...

  • 数据结构:树的实现和遍历(c++)

    (一)二叉树的遍历——递归实现 二叉树常见的遍历方式分为前序遍历、中序遍历和后序遍历。 1 前序遍历 前序遍历也叫...

  • 二叉树的一些基本知识总结

    学了学二叉树,这里说说怎样遍历二叉树.四种方式:前序遍历,中序遍历,后序遍历,层次遍历. 主要说说递归的遍历方法前...

  • 二叉树 基础操作

    二叉树的使用 二叉树结构 先序创建二叉树 DFS 先序遍历二叉树 中序遍历二叉树 后序遍历二叉树 BFS 层次遍历...

  • 二叉树遍历

    二叉树 二叉树的存储结构 前序遍历 中序遍历 后序遍历 遍历代码 反转二叉树 深入学习二叉树 二叉树-你必须要懂!...

  • 二叉树的遍历

    二叉树的常见遍历方式有三种 前序遍历 中序遍历 后序遍历 对于上图所示的二叉树前序遍历结果为:0、1、3、4、2、...

  • 关于二叉树的算法题

    前序遍历中序遍历后序遍历判断是否是平衡二叉树判断是否是对称二叉树判断二叉树高度按照层遍历二叉树判断二叉树宽度

  • 二叉树操作

    树节点 逐行顺序解析二叉树 前序遍历二叉树 中序遍历二叉树 后序遍历二叉树 删除指定数值的节点 前序遍历顺序存储的...

  • 算法系列--二叉树的三种遍历的六种实现

    0. 二叉树是常见的数据结构,二叉树常见的遍历方式有前序遍历,中序遍历和后序遍历。前序遍历就是中-左-右节点的顺序...

网友评论

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

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