算法隨筆-二叉樹遍歷的N種姿式

最近在練習用Python刷算法,leetcode上刷了快300題。一開始懷疑本身根本不會寫代碼,如今以爲會寫一點點了,痛苦又充實的刷題歷程。對我這種半路出家的人而言,收穫真的很大。node

今天就從二叉樹遍歷寫起,曾經有次面試就被迭代實現卡過。。。python

 

 

最簡單的遞歸


#先序遍歷
def preorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        def preTraversal(node,result):
            if node==None:
                return
            #輸出放在最前
            result.append(node.val)
            preTraversal(node.left,result)
            preTraversal(node.right,result)
        preTraversal(root,res)
        return res

#中序遍歷
def inorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        def inorderTraversal(node,result):
            if node==None:
                return
            inorderTraversal(node.left,result)
            #輸出放在中間
            result.append(node.val)
            inorderTraversal(node.right,result)
        inorderTraversal(root,res)
        return res

#後序遍歷
def postorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        def postTraversal(node,result):
            if node==None:
                return
            postTraversal(node.left,result)
            postTraversal(node.right,result)
            #輸出放在最後
            result.append(node.val)
        postTraversal(root,res)
        return res

  遞歸實現極其簡單,可是面試時每每會更進一步,問幾個深刻一點的問題:面試

  1. 遞歸實現存在什麼問題? 
  2. 尾遞歸是什麼?

這兩個問題都在講一件事,就是迭代實現的二叉樹遍歷會存在StackOverflow異常。卻決於操做系統和運行時,不一樣的程序擁有的棧大小不一,可是棧的容量都是較小的,基於遞歸的實現,若是未優化,就會致使堆棧溢出的異常。對.NET而言,系統分配的默認棧空間大小是1MB,樹節點一多,很容就滿了。算法

而尾遞歸則是對普通遞歸的優化,每次迭代最後都是直接調用自身。不少編譯器都對尾遞歸作了生成優化,使得它能夠不在調用棧上面每次都添加一個新的堆棧幀,而是更新它。這樣就不會致使調用棧爆炸的異常。app

若是你能答上上面的問題,每每會讓你寫一下二叉樹遍歷非遞歸的實現,這裏難度就上了一個臺階。post

 

非遞歸實現


 

二叉樹迭代必定會用到棧!二叉樹迭代必定會用到棧!二叉樹迭代必定會用到棧!優化

talk is easy, show you my code操作系統

#超簡單的先序遍歷
def preorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        if root==None:
            return
        stack=[root]
        while stack:
            node=stack.pop()
            res.append(node.val)
            #棧先進後出,順序要注意
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        return res

#稍複雜的中序遍歷
def inorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        curr=root
        stack=[]
        while curr or stack:
            #優先遍歷所有左節點
            while curr:
                stack.append(curr)
                curr=curr.left
            node=stack.pop()
            res.append(node.val)
            #當前節點切換到右節點
            if node.right:
                curr=node.right
        return res

#最複雜的後序遍歷
#解法1 基於先序遍歷的變形  leetcode官方題解:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/er-cha-shu-de-hou-xu-bian-li-by-leetcode/

def postorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        if root==None:
            return res
        stack=[root]
        while stack:
            node=stack.pop()
            res.append(node.val)
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
        #反轉結果
        return res[::-1]

#解法2 記錄走過的路徑
def postorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        if root==None:
            return res
        stack=[root]
        km=set()
        while stack:
            node=stack[-1]
            #只有葉子節點和左右節點被遍歷過的才能夠輸出
            if (node.left==None and node.right==None) or (node.left in km or node.right in km):
                res.append(node.val)
                km.add(node)
                stack.pop()
            else:
                #注意進棧順序
                if node.right:
                    stack.append(node.right)
                if node.left:
                    stack.append(node.left)
        return res

#解法3 中序遍歷的變形,左右子樹遍歷切換
def postorderTraversal(self, root: TreeNode) -> List[int]:
        res=[]
        if root==None:
            return res
        stack=[]
        curr=root
        last=None
        while curr or stack:
            if curr:
                stack.append(curr)
                #切換到左子樹
                curr=curr.left
            else:
                node=stack[-1]
                #是否切換到右子樹
                if node.right and node.right!=last:
                    curr=node.right
                else:
                    res.append(node.val)
                    stack.pop()
                    last=node
        return res

  

後序遍歷的幾種迭代解法很是值得一看,想起當初在白板面前呆了半天也沒寫出來,藍廋香菇,如今能夠自信地講樹的遍歷我掌握了!!!code

相關文章
相關標籤/搜索