Morris遍历——实现二叉树的时间复杂度O(n)空间复杂度O(1)的前序+中序+后序遍历

morris遍历是二叉树遍历算法的超强进阶算法,跟递归、非递归(栈实现)的空间复杂度,morris遍历可以将非递归遍历中的空间复杂度降为O(1)。从而实现时间复杂度为O(N),而空间复杂度为O(1)的精妙算法。
morris遍历利用的是树的叶节点左右孩子为空(树的大量空闲指针),实现空间开销的极限缩减。

 

morris遍历的实现原则

记作当前节点为cur。

  1. 如果cur无左孩子,cur向右移动(cur=cur.right)
  2. 如果cur有左孩子,找到cur左子树上最右的节点,记为mostright
    1. 如果mostright的right指针指向空,让其指向cur,cur向左移动(cur=cur.left)
    2. 如果mostright的right指针指向cur,让其指向空,cur向右移动(cur=cur.right)

morris遍历的实质

建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次。
详细分析二叉树的递归遍历过程,发现在使用系统栈时,对每个节点都有三次回到本身的过程,morris意在借用树的空闲指针去帮助孩子能够重新回到父的这个原本需要借用栈来实现的过程。

基于morris的先序遍历

改写点:按cur的移动顺序进行遍历,但某cur如果是第二次回到则不进行输出。

如何判别cur是否是第二次回到?

  mostright.right的指针指向当前cur时证明当前cur是第二次回到。

Code:

public static void Pre_Morris(Node head)
    {
        if(head==null)
            return;
        Node curNode = head;
        Node mostright = null;
        while(curNode!=null)
        {
            mostright = curNode.left;
            if(mostright!=null)
            {
                //如果当前节点有左子树,则找左子树的最右节点
                while(mostright.right!=null && mostright.right!=curNode)
                {
                    mostright = mostright.right;
                }
                if(mostright.right==null)
                {
                    mostright.right = curNode;
                    System.out.print(curNode.value+"    ");
                    curNode = curNode.left;
                    continue;
                }
                if(mostright.right==curNode)
                {
                    mostright.right = null;
                }
            }
            else {//无左子树,直接打印该节点,然后继续
                System.out.print(curNode.value+"    ");
            }
            curNode = curNode.right;
        }
    }

基于morris的中序遍历

Code

public static void In_Morris(Node head)
    {
        if(head==null)
            return;
        Node curNode = head;
        Node mostright = null;
        while(curNode!=null)
        {
            mostright = curNode.left;
            if(mostright!=null)
            {
                while(mostright.right!=curNode && mostright.right!=null)
                    mostright = mostright.right;
                if(mostright.right==null)
                {
                    mostright.right = curNode;
                    curNode = curNode.left;
                    continue;
                }
                else {
                    mostright.right = null;
                }
            }
            System.out.print(curNode.value+"    ");
            curNode = curNode.right;
        }
    }

基于morris的后序遍历

改写点:根据后序遍历左右中的要求,将打印时机放在第二次回到某cur节点时。此时逆序打印其左子树的右边界。

如何逆序打印某节点的右边界?

  由于需要保证空间复杂度为O(1),且二叉树本身也是一种链表结构,把其右边界看做单向链表,对链表进行逆序后打印,再逆序回来。

Code

public static void Pos_Morris(Node head)
    {
        if(head==null)
            return;
        Node curNode = head;
        Node mostrightNode = null;
        while(curNode!=null)
        {
            mostrightNode = curNode.left;
            if(mostrightNode!=null)
            {
                while(mostrightNode.right!=curNode && mostrightNode.right!=null)
                    mostrightNode = mostrightNode.right;
                if(mostrightNode.right==null)
                {
                    mostrightNode.right = curNode;
                    curNode = curNode.left;
                    continue;
                }
                else {
                    mostrightNode.right = null;
                    printEdge(curNode.left);
                }
            }
            curNode = curNode.right;
        }
        printEdge(head);
        
    }
    public static void printEdge(Node node)
    {
        Node tempNode = reverse(node);//对右边界逆序
        Node curNode = tempNode;
        while(curNode!=null)
        {
            System.out.print(curNode.value+"    ");
            curNode = curNode.right;
        }
        reverse(tempNode);//将右边界再一次逆序(恢复初始状态)
    }
    public static Node reverse(Node node)
    {
        Node preNode = null;
        Node nextNode = null;
        while(node!=null)
        {
            nextNode = node.right;
            node.right = preNode;
            preNode = node;
            node = nextNode;
        }
        return preNode;
    }
posted @ 2021-02-13 14:57  γGama  阅读(759)  评论(0)    收藏  举报