最近在練習用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
遞歸實現極其簡單,可是面試時每每會更進一步,問幾個深刻一點的問題:面試
這兩個問題都在講一件事,就是迭代實現的二叉樹遍歷會存在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