Morris遍歷

Morris主要解決一個問題,如何實現二叉樹的前中後序遍歷,有兩個要求:

1. O(1)空間複雜度,O(N)時間複雜度;

2. 二叉樹的形狀不能被破壞(中間過程允許改變其形狀)。

以往是用棧結構輔助實現從下到上,但此算法是用到了線索二叉樹的概念,通過讓底層結點指向null的空閒指針指向上層的某個結點。

先不論先中後序,Morris遍歷的過程爲:

1、如果cur 爲null,則過程停止,否則繼續;

2、如果cur沒有左子樹,則cur = cur.right;

3、如果cur有左子樹,則找到cur左子樹上最右的結點,記爲mostRight

1)如果mostRight的right指針指向null,則mostRight.right = cur,然後cur = cur.left

2)如果mostRight的right指針指向cur(即已經經過第一次遍歷),令mostRight.right = null,cur = cur.right

根據以上過程寫出的代碼爲:

    def Morris(self,root):
        if not root:
            return None
        cur = root
        while cur:
            mostRight = cur.left
            if mostRight:
                while mostRight.right and mostRight.right != cur:
                    mostRight = mostRight.right
                if mostRight.right == None:
                    mostRight.right = cur
                    cur = cur.left
                    continue
                else:
                    mostRight.right = None
            cur = cur.right

那麼先序遍歷,則是將打印函數放在第一次遍歷到的結點處:

    def Morris(self,root):
        if not root:
            return None
        cur = root
        while cur:
            mostRight = cur.left
            if mostRight:
                while mostRight.right and mostRight.right != cur:
                    mostRight = mostRight.right
                if mostRight.right == None:
                    mostRight.right = cur
                    print(cur.val)#這裏是根結點
                    cur = cur.left
                    continue
                else:
                    mostRight.right = None
            else:
                print(cur.val)#無左就直接打印右
            cur = cur.right

中序遍歷,則是等左子樹都遍歷完成後再開始打印

    def Morris(self,root):
        if not root:
            return None
        cur = root
        while cur:
            mostRight = cur.left
            if mostRight:
                while mostRight.right and mostRight.right != cur:
                    mostRight = mostRight.right
                if mostRight.right == None:
                    mostRight.right = cur
                    cur = cur.left
                    continue
                else:
                    mostRight.right = None
            print(cur.val)
            cur = cur.right

後序比較複雜,程序如下:

1、對於無左子樹的(只遍歷一次的),直接跳過,無打印行爲;

2、對於有左子樹的(可以遍歷兩次的),cur第一次時無打印,第二次時逆序打印左子樹的右邊界;

3、cur遍歷完成後,逆序打印整個樹的右邊界。

其實整個程序就如下圖所示:

打印順序爲4,5,2,6,7,3,1

    def Morris(self,root):
        if not root:
            return None
        cur = root
        while cur:
            mostRight = cur.left
            if mostRight:#有左子樹,可以遍歷兩次
                while mostRight.right and mostRight.right != cur:
                    mostRight = mostRight.right
                if mostRight.right == None:
                    mostRight.right = cur
                    cur = cur.left
                    continue
                else:
                    mostRight.right = None
                    #第二次
                    self.printEdge(cur.left)
            cur = cur.right
        # 最後逆序打印整棵樹右邊界
        self.printEdge(root)
    def printEdge(self,head):
        tail = self.reverseEdge(head)
        cur = tail
        while cur:
            print(cur.val)
            cur = cur.right
        #不能改變二叉樹結構,逆序完了逆回去
        self.reverseEdge(tail)
    def reverseEdge(self,cur):
        pre = None
        while cur:
            next = cur.right
            cur.right = pre
            pre = cur
            cur = next
        return pre

最後證明一下Morris符合題目要求:

代碼只使用了有限幾個變量,故額外空間複雜度爲O(1),而遍歷對於有左子樹的只遍歷兩次,無左子樹的只遍歷一次,總時間複雜度還是O(N)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章