後序+中序(前序+中序)重構樹,嚴格O(N)演算法
105/106. Construct Binary Tree from Inorder and Postorder(Preorder) Traversal
根據前序,中序或者後序,中序重構樹。這裡僅以後序+中序進行分析。文末給出前序的類似程式碼,原理一致。
Notes
- 遞迴的難點在於遞迴函式的正確定義
- 理解定義再理解程式碼非常簡單,尤其是二叉樹,對於左子樹的遞迴,正確的理解是,左子樹任務已經完成了,我需要做什麼
- 設計遞迴函式,可以從基元考慮(沒有子節點,邊界節點)和普遍情況考慮(即左子節點全部完成,右子節點全部完成),前者是自底向上,後者是自頂向下的思維。
Version 1
回到重構二叉樹的問題,這其實是個常見的簡單問題,重點就是把座標細節搞定。
- 新建當前節點
- 找到左子樹範圍,重構左子樹
- 找到右子樹範圍,重構右子樹
# version 1
# 遞迴構造
# 時間,每層時間和N,深度最多N,一般情況是logN,所以最差N^2,一般情況NlogN
# 額外空間,無
# Runtime: 356 ms, faster than 10.68% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 18.7 MB, less than 60.97% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
for idx in range(il, ih):
if inorder[idx] == root.val: break
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
Version 2
利用map來保證 O ( N ) O(N) O(N)的時間效率。你永遠都可以考慮優先用空間換時間。
# version 2
# 空間換時間
# Runtime: 52 ms, faster than 66.41% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 19.4 MB, less than 53.98% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
num2idx = dict(zip(inorder, range(len(inorder))))
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
idx = num2idx[root.val]
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
Version 3
一種嚴格的 O ( N ) O(N) O(N)演算法。非常優雅的實現。
理解該演算法並不容易,要點就是要記住遞迴函式的定義,然後相信它。它不再是單獨重構某一邊的子樹,而是定義為:給定stop,高速你在inoreder找到什麼停止,函式負責把我的整顆樹從陣列裡重構出來。
所有節點pop一次,所以複雜度 O ( N ) O(N) O(N).
話又說回來,遞迴演算法,定義是最重要的,思考的關鍵是相信定義。可是,這比較適合用來理解遞迴程式碼。設計還是很難,這道題的實現非常優雅。
# version 3 O(n)的演算法
# Runtime: 32 ms, faster than 99.61% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 17.7 MB, less than 94.95% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
# 告知stop,從二個陣列重構整顆樹,初始的stop為None
def rebuild(stop):
if inorder[-1] != stop:
# 新建當前節點
root = TreeNode(val=postorder.pop())
# 重構右子樹,inoreder的stop為當前節點的值,找到則右子樹重構完畢
root.right = rebuild(root.val)
# 注意右子樹重構完畢,意味著inorder中已經不存在左子樹的節點了
# 那麼現在該幹什麼?當然是pop掉當前的節點了
inorder.pop()
# 重構左子樹,左子樹的stop值為本函式的stop相同,初始為None
root.left = rebuild(stop)
return root
# 哨兵節點
inorder = [None] + inorder
return rebuild(None)
附,前序+中序的重構
# version 1
# 遞迴構造
# Runtime: 356 ms, faster than 10.68% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 18.7 MB, less than 60.97% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
for idx in range(il, ih):
if inorder[idx] == root.val: break
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
# version 2
# 空間換時間
# Runtime: 52 ms, faster than 66.41% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 19.4 MB, less than 53.98% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
num2idx = dict(zip(inorder, range(len(inorder))))
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
idx = num2idx[root.val]
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
# version 3 O(n)的演算法
# Runtime: 32 ms, faster than 99.61% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 17.7 MB, less than 94.95% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
# 告知stop,從二個陣列重構整顆樹,初始的stop為None
def rebuild(stop):
if inorder[-1] != stop:
# 新建當前節點
root = TreeNode(val=postorder.pop())
# 重構右子樹,inoreder的stop為當前節點的值,找到則右子樹重構完畢
root.right = rebuild(root.val)
# 注意右子樹重構完畢,意味著inorder中已經不存在左子樹的節點了
# 那麼現在該幹什麼?當然是pop掉當前的節點了
inorder.pop()
# 重構左子樹,左子樹的stop值為本函式的stop相同,初始為None
root.left = rebuild(stop)
return root
# 哨兵節點
inorder = [None] + inorder
return rebuild(None)
Ref
- https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/discuss/34543/Simple-O(n)-without-map
相關文章
- 二叉樹 前序、中序、後序二叉樹
- 【二叉樹】前中序求後序,中後序求前序二叉樹
- 刷題筆記:樹的前序、中序、後序遍歷筆記
- 二叉樹 ---- 前序 中序 後序 知二求一二叉樹
- 二叉樹的前序、中序、後序三種遍歷二叉樹
- 【資料結構與演算法】二叉樹的 Morris 遍歷(前序、中序、後序)資料結構演算法二叉樹
- 先序、中序、後序序列的二叉樹構造演算法二叉樹演算法
- 二叉樹的前序,中序,後序遍歷方法總結二叉樹
- 二叉樹迭代器(中序遞迴、前序和後序遍歷)演算法二叉樹遞迴演算法
- 【樹01】對二叉樹前序/中序/後序遍歷演算法的一些思考二叉樹演算法
- (樹)根據中序後序構建二叉樹二叉樹
- 從前序與中序構造二叉樹二叉樹
- 二叉樹的前序、中序、後序的遞迴和迭代實現二叉樹遞迴
- 二叉樹建立,前序遍歷,中序遍歷,後序遍歷 思路二叉樹
- 二叉樹的建立、前序遍歷、中序遍歷、後序遍歷二叉樹
- 資料結構與演算法——二叉樹的前序遍歷,中序遍歷,後序遍歷資料結構演算法二叉樹
- 還原二叉樹(先序+中序-〉後序)二叉樹
- 演算法 -- 實現二叉樹先序,中序和後序遍歷演算法二叉樹
- 二叉樹先知道後序和中序,求先序二叉樹
- 二叉樹前序、中序、後序遍歷相互求法(code留著看,概念先看了)二叉樹
- 二叉樹的四種遍歷方法:先序,中序,後序,層序二叉樹
- 從中序與後序遍歷序列構造二叉樹二叉樹
- 【演算法】二叉樹、N叉樹先序、中序、後序、BFS、DFS遍歷的遞迴和迭代實現記錄(Java版)演算法二叉樹遞迴Java
- 已知二叉樹的先序和後序求任意一中序二叉樹
- 二叉樹中序和後序遍歷表示式二叉樹
- 中序先序到後序 洛谷1827
- 遞迴和迭代實現二叉樹先序、中序、後序和層序遍歷遞迴二叉樹
- LintCode 前序遍歷和中序遍歷樹構造二叉樹二叉樹
- 非遞迴遍歷二叉樹的四種策略-先序、中序、後序和層序遞迴二叉樹
- 889. 根據前序和後序遍歷構造二叉樹二叉樹
- PHP基於非遞迴演算法實現先序、中序及後序遍歷二叉樹操作示例PHP遞迴演算法二叉樹
- 刷題系列 - 中序和後序遍歷佇列,構造對應二叉樹;佇列二叉樹
- 【根據前序和中序遍歷構造二叉樹】棧+迭代 || 遞迴二叉樹遞迴
- 資料結構實驗之二叉樹八:(中序後序)求二叉樹的深度資料結構二叉樹
- 根據二叉樹的先序序列和中序序列還原二叉樹並列印後序序列二叉樹
- 根據前序遍歷序列、中序遍歷序列,重建二叉樹二叉樹
- 106. 從中序與後序遍歷序列構造二叉樹——Java實現二叉樹Java
- LeetCode-106-從中序與後序遍歷序列構造二叉樹LeetCode二叉樹