重建:從遍歷序列到樹(shù)結(jié)構(gòu)的遞歸構(gòu)建與工程優(yōu)化)
1. 項(xiàng)目概述二叉樹(shù)重建的“施工藍(lán)圖”在數(shù)據(jù)結(jié)構(gòu)的世界里二叉樹(shù)就像一座精巧的建筑。我們常常會(huì)得到關(guān)于這座建筑的兩種“圖紙”一種是描繪了訪問(wèn)房間順序的“遍歷序列”另一種則是記錄了房間之間父子關(guān)系的“結(jié)構(gòu)信息”。而“根據(jù)先序或后序和中序遍歷序列重建二叉樹(shù)”這個(gè)問(wèn)題本質(zhì)上就是給你兩份不同的圖紙讓你還原出這座建筑原本的結(jié)構(gòu)。這不僅是數(shù)據(jù)結(jié)構(gòu)與算法課程中的經(jīng)典考題更是理解遞歸思想、指針操作和樹(shù)形結(jié)構(gòu)內(nèi)在邏輯的絕佳練兵場(chǎng)。很多朋友在初次接觸時(shí)會(huì)覺(jué)得遞歸調(diào)用像一團(tuán)亂麻指針指來(lái)指去讓人頭暈。今天我就以一個(gè)老碼農(nóng)的視角帶你從“施工隊(duì)”的角度徹底拆解這個(gè)建樹(shù)過(guò)程把每一步為什么這么做、怎么想清楚講得明明白白。無(wú)論你是正在備戰(zhàn)面試還是想夯實(shí)基礎(chǔ)這篇超詳講解都能讓你從“看懂”到“通透”最后能自己閉著眼睛“施工”。2. 核心原理兩張“圖紙”如何定義一棵樹(shù)要重建首先得明白我們手里的“圖紙”——遍歷序列——到底記錄了樹(shù)的什么信息。這比直接背算法模板重要得多。2.1 遍歷序列的信息密碼一棵二叉樹(shù)的遍歷無(wú)非是按照某種規(guī)則訪問(wèn)每個(gè)節(jié)點(diǎn)。先序、中序、后序的區(qū)別就在于訪問(wèn)根節(jié)點(diǎn)的時(shí)機(jī)。先序遍歷 (Preorder)它的訪問(wèn)順序是根節(jié)點(diǎn) - 左子樹(shù) - 右子樹(shù)。這意味著在先序序列中第一個(gè)元素一定是整棵樹(shù)的根節(jié)點(diǎn)。這是一個(gè)非常強(qiáng)的定位信息。中序遍歷 (Inorder)它的訪問(wèn)順序是左子樹(shù) - 根節(jié)點(diǎn) - 右子樹(shù)。這是一個(gè)關(guān)鍵的特性對(duì)于中序序列中的任意一個(gè)節(jié)點(diǎn)在它左邊的所有節(jié)點(diǎn)都屬于它的左子樹(shù)在它右邊的所有節(jié)點(diǎn)都屬于它的右子樹(shù)。這提供了“左右劃分”的信息。后序遍歷 (Postorder)它的訪問(wèn)順序是左子樹(shù) - 右子樹(shù) - 根節(jié)點(diǎn)。與先序?qū)?yīng)在后序序列中最后一個(gè)元素一定是整棵樹(shù)的根節(jié)點(diǎn)。注意單獨(dú)任何一種遍歷序列都無(wú)法唯一確定一棵樹(shù)。比如先序序列[1, 2]它可能對(duì)應(yīng)根為1左孩子為2的樹(shù)也可能對(duì)應(yīng)根為1右孩子為2的樹(shù)。必須結(jié)合能提供左右子樹(shù)劃分信息的序列通常是中序才能唯一確定。2.2 重建的基石遞歸分解重建算法的核心思想是“分而治之”的遞歸。定位根節(jié)點(diǎn)利用先序第一個(gè)元素或后序最后一個(gè)元素確定當(dāng)前子樹(shù)的根。劃分左右子樹(shù)在中序序列中找到這個(gè)根節(jié)點(diǎn)其左側(cè)序列即為左子樹(shù)的中序遍歷結(jié)果右側(cè)即為右子樹(shù)的中序遍歷結(jié)果。計(jì)算子樹(shù)規(guī)模根據(jù)劃分出的左子樹(shù)中序序列的長(zhǎng)度我們就能從先序/后序序列中精確地分離出對(duì)應(yīng)左子樹(shù)和右子樹(shù)的先序/后序序列。遞歸構(gòu)建將左子樹(shù)和右子樹(shù)各自看作一棵新的、規(guī)模更小的樹(shù)重復(fù)步驟1-3直到序列為空即遇到了空節(jié)點(diǎn)。這個(gè)過(guò)程就像施工先找到地基根然后根據(jù)圖紙中序畫(huà)出左翼和右翼的邊界最后對(duì)左右兩翼分別進(jìn)行同樣的施工流程。2.3 為什么必須要有中序序列這是一個(gè)常見(jiàn)困惑。我們?cè)囅胫挥邢刃騕1, 2, 3]和后序[2, 3, 1]。我們知道根是1但剩下的[2, 3]屬于左還是右無(wú)法判斷。因?yàn)橄刃蚝秃笮蚨贾幻鞔_了根的位置但沒(méi)有提供節(jié)點(diǎn)在“水平方向”左右的分布信息。而中序序列的“左-根-右”特性天然地完成了這個(gè)水平切分所以它是重建的必要條件在已知先序后序且樹(shù)不唯一的情況下需要其他條件如真二叉樹(shù)才能確定但那是特例。3. 先序 中序 建樹(shù)詳解我們先攻克更常見(jiàn)的“先序中序”組合。我會(huì)用一個(gè)具體的例子貫穿始終并給出帶詳細(xì)注釋的代碼。假設(shè)先序遍歷序列preorder [3, 9, 20, 15, 7]中序遍歷序列inorder [9, 3, 15, 20, 7]我們的目標(biāo)是重建出如下二叉樹(shù)3 / \ 9 20 / \ 15 73.1 手動(dòng)推演理解遞歸過(guò)程第一層遞歸構(gòu)建整棵樹(shù)根據(jù)先序序列當(dāng)前子樹(shù)的根節(jié)點(diǎn)是preorder[0] 3。在中序序列inorder中找到3發(fā)現(xiàn)其索引為1從0開(kāi)始。劃分左子樹(shù)的中序序列3左邊的部分[9]長(zhǎng)度為1。右子樹(shù)的中序序列3右邊的部分[15, 20, 7]長(zhǎng)度為3。推導(dǎo)子樹(shù)的先序序列先序序列的結(jié)構(gòu)是[根 (左子樹(shù)部分) (右子樹(shù)部分)]。我們已經(jīng)知道根是3左子樹(shù)有1個(gè)節(jié)點(diǎn)右子樹(shù)有3個(gè)節(jié)點(diǎn)。因此左子樹(shù)的先序序列是preorder中根之后長(zhǎng)度為1的部分[9]。右子樹(shù)的先序序列是剩下的部分[20, 15, 7]?,F(xiàn)在問(wèn)題變成了用左先序[9],左中序[9]構(gòu)建左子樹(shù)。用右先序[20,15,7],右中序[15,20,7]構(gòu)建右子樹(shù)。第二層遞歸構(gòu)建右子樹(shù)以它為例當(dāng)前右子樹(shù)的根節(jié)點(diǎn)是右先序[0] 20。在右中序[15,20,7]中找到20索引為1。劃分左子樹(shù)相對(duì)于節(jié)點(diǎn)20的中序[15]長(zhǎng)度1。右子樹(shù)相對(duì)于節(jié)點(diǎn)20的中序[7]長(zhǎng)度1。推導(dǎo)右先序[20,15,7]左子樹(shù)的先序根20之后長(zhǎng)度為1的部分[15]。右子樹(shù)的先序剩下的部分[7]。繼續(xù)遞歸構(gòu)建節(jié)點(diǎn)20的左子樹(shù)用[15]和[15]和右子樹(shù)用[7]和[7]。這兩個(gè)遞歸都會(huì)直接創(chuàng)建葉子節(jié)點(diǎn)并返回。左子樹(shù)的構(gòu)建過(guò)程類似最終所有遞歸觸底序列為空或只有一個(gè)元素整棵樹(shù)構(gòu)建完成。3.2 代碼實(shí)現(xiàn)與逐行解析這里給出Python的遞歸實(shí)現(xiàn)它最直觀地反映了上述思想。# Definition for a binary tree node. class TreeNode: def __init__(self, val0, leftNone, rightNone): self.val val self.left left self.right right class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) - TreeNode: # 遞歸終止條件如果序列為空則對(duì)應(yīng)空節(jié)點(diǎn) if not preorder or not inorder: return None # 1. 定位根節(jié)點(diǎn)先序序列的第一個(gè)元素 root_val preorder[0] root TreeNode(root_val) # 2. 在中序序列中找到根節(jié)點(diǎn)的位置 # 這里使用 index() 方法在實(shí)際面試或高性能場(chǎng)景下可先用哈希表記錄中序值到索引的映射將查找復(fù)雜度從O(n)降為O(1) root_index_in_inorder inorder.index(root_val) # 3. 切割中序序列得到左右子樹(shù)的中序序列 left_inorder inorder[:root_index_in_inorder] # 左子樹(shù)中序 right_inorder inorder[root_index_in_inorder 1:] # 右子樹(shù)中序 # 4. 切割先序序列。關(guān)鍵點(diǎn)左右子樹(shù)的先序序列長(zhǎng)度與其中序序列長(zhǎng)度相同。 left_preorder preorder[1:1 len(left_inorder)] # 左子樹(shù)先序 right_preorder preorder[1 len(left_inorder):] # 右子樹(shù)先序 # 5. 遞歸構(gòu)建左右子樹(shù) root.left self.buildTree(left_preorder, left_inorder) root.right self.buildTree(right_preorder, right_inorder) # 6. 返回當(dāng)前樹(shù)的根節(jié)點(diǎn) return root關(guān)鍵點(diǎn)與注意事項(xiàng)序列切割的索引計(jì)算這是最容易出錯(cuò)的地方。left_preorder的起始索引是1跳過(guò)根結(jié)束索引是1 len(left_inorder)。一定要確保切割出的子序列長(zhǎng)度與對(duì)應(yīng)的中序子序列長(zhǎng)度一致這是遞歸正確的保證。遞歸終止條件當(dāng)傳入的preorder或inorder為空列表時(shí)說(shuō)明應(yīng)該構(gòu)建一個(gè)空節(jié)點(diǎn)None。這是遞歸的“觸底”時(shí)刻。時(shí)間復(fù)雜度優(yōu)化代碼中inorder.index(root_val)在最壞情況下樹(shù)退化成鏈表會(huì)使算法復(fù)雜度達(dá)到 O(n2)。一個(gè)非常重要的優(yōu)化技巧是預(yù)處理在遞歸開(kāi)始前遍歷一次中序序列用一個(gè)字典val_to_index把每個(gè)值對(duì)應(yīng)的索引記錄下來(lái)。這樣在遞歸過(guò)程中查找根節(jié)點(diǎn)位置就是 O(1) 的操作整體復(fù)雜度優(yōu)化到 O(n)。這是面試中展示你思維嚴(yán)密性的加分項(xiàng)。# 優(yōu)化版本使用哈希表加速查找 class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) - TreeNode: # 構(gòu)建中序值到索引的映射 inorder_index_map {val: idx for idx, val in enumerate(inorder)} def helper(pre_left, pre_right, in_left, in_right): 遞歸輔助函數(shù)通過(guò)索引范圍來(lái)操作避免頻繁切片創(chuàng)建新列表 if pre_left pre_right: # 范圍無(wú)效說(shuō)明為空樹(shù) return None # 當(dāng)前子樹(shù)的根節(jié)點(diǎn) root_val preorder[pre_left] root TreeNode(root_val) # 在中序映射中查找根節(jié)點(diǎn)位置 in_root_idx inorder_index_map[root_val] # 計(jì)算左子樹(shù)的大小 left_subtree_size in_root_idx - in_left # 遞歸構(gòu)建左右子樹(shù) # 左子樹(shù)在先序中的范圍[pre_left1, pre_leftleft_subtree_size] # 左子樹(shù)在中序中的范圍[in_left, in_root_idx-1] root.left helper(pre_left 1, pre_left left_subtree_size, in_left, in_root_idx - 1) # 右子樹(shù)在先序中的范圍[pre_leftleft_subtree_size1, pre_right] # 右子樹(shù)在中序中的范圍[in_root_idx1, in_right] root.right helper(pre_left left_subtree_size 1, pre_right, in_root_idx 1, in_right) return root n len(preorder) return helper(0, n - 1, 0, n - 1)這個(gè)優(yōu)化版本避免了遞歸過(guò)程中昂貴的列表切片操作直接使用索引范圍在原數(shù)組上操作空間和時(shí)間效率都更高是更工程化的寫(xiě)法。4. 后序 中序 建樹(shù)詳解理解了先序中序后序中序就觸類旁通了。核心邏輯完全一致只是“根”的位置從序列頭部移到了尾部。假設(shè)后序遍歷序列postorder [9, 15, 7, 20, 3]中序遍歷序列inorder [9, 3, 15, 20, 7]重建同一棵樹(shù)3 / \ 9 20 / \ 15 74.1 手動(dòng)推演對(duì)比第一層遞歸根據(jù)后序序列當(dāng)前子樹(shù)的根節(jié)點(diǎn)是postorder的最后一個(gè)元素3。在中序序列中找到3索引為1。劃分中序序列左子樹(shù)中序[9]長(zhǎng)度1。右子樹(shù)中序[15, 20, 7]長(zhǎng)度3。關(guān)鍵推導(dǎo)后序序列后序序列的結(jié)構(gòu)是[(左子樹(shù)部分) (右子樹(shù)部分) 根]。左子樹(shù)后序?qū)?yīng)左子樹(shù)中序的長(zhǎng)度從postorder開(kāi)頭取1個(gè)元素[9]。右子樹(shù)后序剩下的、去掉最后一個(gè)根元素的部分即postorder中從索引1到倒數(shù)第二個(gè)元素[15, 7, 20]等等這里要小心右子樹(shù)的后序序列應(yīng)該是[15, 7, 20]嗎我們驗(yàn)證一下右子樹(shù)[20, 15, 7]的后序遍歷結(jié)果確實(shí)是[15, 7, 20]。所以推導(dǎo)正確。遞歸構(gòu)建用(左后序[9], 左中序[9])和(右后序[15,7,20], 右中序[15,20,7])分別構(gòu)建左右子樹(shù)。4.2 代碼實(shí)現(xiàn)class Solution: def buildTree(self, inorder: List[int], postorder: List[int]) - TreeNode: if not inorder or not postorder: return None # 1. 定位根節(jié)點(diǎn)后序序列的最后一個(gè)元素 root_val postorder[-1] root TreeNode(root_val) # 2. 在中序序列中找到根節(jié)點(diǎn)位置 root_index_in_inorder inorder.index(root_val) # 3. 切割中序序列 left_inorder inorder[:root_index_in_inorder] right_inorder inorder[root_index_in_inorder 1:] # 4. 切割后序序列 # 左子樹(shù)后序長(zhǎng)度 左子樹(shù)中序長(zhǎng)度 left_postorder postorder[:len(left_inorder)] # 右子樹(shù)后序 剩下的部分排除掉最后一個(gè)根元素 right_postorder postorder[len(left_inorder): -1] # 5. 遞歸構(gòu)建 root.left self.buildTree(left_inorder, left_postorder) root.right self.buildTree(right_inorder, right_postorder) return root后序建樹(shù)的核心注意點(diǎn)切割后序序列時(shí)right_postorder的結(jié)束索引是-1這意味著不包含最后一個(gè)元素即當(dāng)前的根節(jié)點(diǎn)。這個(gè)細(xì)節(jié)必須準(zhǔn)確把握否則序列對(duì)應(yīng)關(guān)系會(huì)錯(cuò)亂導(dǎo)致遞歸失敗或結(jié)果錯(cuò)誤。同樣地這里也強(qiáng)烈推薦使用索引哈希表的優(yōu)化方法避免切片和線性查找。# 后序中序的優(yōu)化版本索引法 class Solution: def buildTree(self, inorder: List[int], postorder: List[int]) - TreeNode: index_map {val: idx for idx, val in enumerate(inorder)} def helper(in_left, in_right, post_left, post_right): if in_left in_right or post_left post_right: return None # 根節(jié)點(diǎn)是后序序列的最后一個(gè)元素 root_val postorder[post_right] root TreeNode(root_val) in_root_idx index_map[root_val] # 左子樹(shù)節(jié)點(diǎn)數(shù) left_size in_root_idx - in_left # 遞歸構(gòu)建 # 左子樹(shù)后序范圍[post_left, post_left left_size - 1] # 左子樹(shù)中序范圍[in_left, in_root_idx - 1] root.left helper(in_left, in_root_idx - 1, post_left, post_left left_size - 1) # 右子樹(shù)后序范圍[post_left left_size, post_right - 1] # 右子樹(shù)中序范圍[in_root_idx 1, in_right] root.right helper(in_root_idx 1, in_right, post_left left_size, post_right - 1) return root n len(inorder) return helper(0, n - 1, 0, n - 1)5. 邊界條件與常見(jiàn)陷阱排查在實(shí)際編碼和面試中除了核心邏輯邊界條件和一些隱蔽的陷阱是決定成敗的關(guān)鍵。5.1 輸入合法性檢查序列長(zhǎng)度不一致如果給定的先序/后序序列與中序序列長(zhǎng)度不同那么輸入本身就是無(wú)效的應(yīng)該立即返回錯(cuò)誤或空樹(shù)??梢栽诤瘮?shù)入口處添加檢查。序列元素不匹配理論上兩個(gè)序列應(yīng)包含完全相同的元素集。如果在中序序列中找不到先序/后序序列指定的根節(jié)點(diǎn)說(shuō)明輸入有誤。使用index()方法時(shí)這會(huì)引發(fā)ValueError使用哈希表時(shí)可以提前判斷if root_val not in index_map:??蛰斎脒@是遞歸終止條件的一部分必須處理。傳入空列表應(yīng)返回None。5.2 遞歸過(guò)程中的易錯(cuò)點(diǎn)索引計(jì)算錯(cuò)誤這是最高發(fā)的錯(cuò)誤。尤其是在自己推導(dǎo)切片范圍時(shí)一定要用一個(gè)小例子比如3個(gè)節(jié)點(diǎn)的樹(shù)在紙上畫(huà)圖驗(yàn)證。記住核心原則左/右子樹(shù)的先序/后序子序列長(zhǎng)度必須等于其對(duì)應(yīng)的中序子序列長(zhǎng)度。忽略遞歸終止條件忘記處理序列為空的情況會(huì)導(dǎo)致遞歸無(wú)限進(jìn)行或索引越界?;煜刃蚝秃笮虻母恢镁o張時(shí)容易寫(xiě)錯(cuò)記住“先序頭后序尾”。使用index()方法的性能陷阱如前所述在未優(yōu)化的遞歸中每次都在中序列表里線性查找對(duì)于深度為n的退化樹(shù)復(fù)雜度是 O(n2)。面試時(shí)如果被問(wèn)到優(yōu)化一定要能說(shuō)出哈希表預(yù)處理的方法。5.3 調(diào)試技巧與驗(yàn)證方法當(dāng)你覺(jué)得程序邏輯沒(méi)錯(cuò)但結(jié)果不對(duì)時(shí)可以嘗試以下方法最小用例測(cè)試用只有一個(gè)節(jié)點(diǎn)[1]和[1]的輸入測(cè)試這是最簡(jiǎn)單的基準(zhǔn)。三層完全二叉樹(shù)測(cè)試用一棵簡(jiǎn)單的三層滿二叉樹(shù)7個(gè)節(jié)點(diǎn)來(lái)測(cè)試。手動(dòng)寫(xiě)出它的各種遍歷序列然后用你的程序重建看結(jié)果是否一致。打印遞歸日志在遞歸函數(shù)入口打印當(dāng)前的序列或索引范圍觀察遞歸的展開(kāi)和收縮過(guò)程是否符合預(yù)期。這能幫你快速定位在哪一層遞歸出現(xiàn)了序列切割錯(cuò)誤。重建后驗(yàn)證編寫(xiě)一個(gè)簡(jiǎn)單的樹(shù)遍歷函數(shù)如先序遍歷將重建出的樹(shù)再遍歷一遍得到的序列是否與輸入的先序序列一致。這是最直接的驗(yàn)證。6. 從理解到精通舉一反三與擴(kuò)展思考掌握了基礎(chǔ)重建我們可以看看一些變種和擴(kuò)展問(wèn)題這能加深你對(duì)這個(gè)模型的理解。6.1 擴(kuò)展問(wèn)題根據(jù)先序和后序能否建樹(shù)如前所述僅憑先序和后序通常無(wú)法確定唯一的二叉樹(shù)。但有一個(gè)特例如果這是一棵真二叉樹(shù)即每個(gè)節(jié)點(diǎn)的度數(shù)為0或2沒(méi)有只有一個(gè)孩子的節(jié)點(diǎn)那么先序和后序可以唯一確定這棵樹(shù)。推導(dǎo)邏輯類似但需要更巧妙的判斷。核心在于在先序序列中根節(jié)點(diǎn)之后的那個(gè)元素preorder[1]它可能是左子樹(shù)的根如果左子樹(shù)存在。同時(shí)在后序序列中這個(gè)preorder[1]元素也一定會(huì)出現(xiàn)并且它可以將后序序列分割成左右子樹(shù)的部分。這是一個(gè)更進(jìn)階的挑戰(zhàn)理解了先序中序的原理后你可以嘗試推導(dǎo)一下。6.2 迭代解法簡(jiǎn)介除了遞歸這個(gè)問(wèn)題也可以用迭代法配合棧來(lái)解決。思路是模擬先序遍歷的過(guò)程用指針i指向先序序列依次作為根用指針j指向中序序列用來(lái)判斷當(dāng)前節(jié)點(diǎn)是否有左孩子。遍歷先序序列將當(dāng)前節(jié)點(diǎn)入棧。如果棧頂節(jié)點(diǎn)的值不等于中序序列j指向的值說(shuō)明當(dāng)前節(jié)點(diǎn)還有左孩子根據(jù)中序“左-根-右”還沒(méi)到根。如果相等則說(shuō)明棧頂節(jié)點(diǎn)沒(méi)有左孩子或者左子樹(shù)已處理完應(yīng)該出棧并讓j后移然后處理右子樹(shù)。迭代法的代碼相對(duì)繞一些但好處是避免了遞歸的??臻g開(kāi)銷并且是另一種思維模式的訓(xùn)練。我建議在徹底掌握遞歸解法后再去研究迭代解法。6.3 在真實(shí)場(chǎng)景中的應(yīng)用你可能會(huì)問(wèn)這個(gè)算法除了做題還有什么用一個(gè)典型的應(yīng)用場(chǎng)景是數(shù)據(jù)的序列化與反序列化。當(dāng)我們需要將一棵二叉樹(shù)存儲(chǔ)到文件或通過(guò)網(wǎng)絡(luò)傳輸時(shí)通常會(huì)將其轉(zhuǎn)化為一個(gè)線性序列比如先序序列并用特殊符號(hào)表示空節(jié)點(diǎn)。在接收端我們需要根據(jù)這個(gè)序列重新構(gòu)建出樹(shù)結(jié)構(gòu)。雖然通常的序列化會(huì)包含空節(jié)點(diǎn)信息以唯一確定樹(shù)但其核心思想與遍歷重建是相通的。理解遍歷序列與樹(shù)結(jié)構(gòu)的對(duì)應(yīng)關(guān)系是處理樹(shù)形數(shù)據(jù)的基礎(chǔ)。7. 實(shí)操心得與避坑指南最后分享幾點(diǎn)我踩過(guò)坑才得來(lái)的經(jīng)驗(yàn)紙上得來(lái)終覺(jué)淺絕知此事要躬行一定要在紙上畫(huà)圖。畫(huà)一棵簡(jiǎn)單的樹(shù)寫(xiě)出它的先序、中序、后序序列。然后手動(dòng)按照算法步驟去切割序列、遞歸直到重建出原樹(shù)。這個(gè)過(guò)程做兩遍比看十遍代碼都管用。從“會(huì)寫(xiě)”到“會(huì)講”面試時(shí)面試官不僅要看你的代碼更看重你的思路。在寫(xiě)代碼前先用語(yǔ)言把“定位根 - 中序劃分左右 - 計(jì)算長(zhǎng)度 - 切割另一序列 - 遞歸”這個(gè)流程清晰地講出來(lái)。這能體現(xiàn)你邏輯的條理性。主動(dòng)提出優(yōu)化即使題目沒(méi)要求在寫(xiě)出基礎(chǔ)遞歸解法后可以主動(dòng)說(shuō)“這個(gè)解法在極端情況下時(shí)間復(fù)雜度是 O(n2)我們可以通過(guò)預(yù)先建立中序值到索引的哈希表來(lái)優(yōu)化到 O(n)?!?這絕對(duì)是亮眼的表現(xiàn)。測(cè)試用例要全面不要只測(cè)正常情況。要測(cè)試空樹(shù)、單節(jié)點(diǎn)樹(shù)、只有左子樹(shù)的鏈表、只有右子樹(shù)的鏈表、完全二叉樹(shù)。這些邊界case能幫你發(fā)現(xiàn)代碼中的潛在問(wèn)題。理解本質(zhì)而非背誦模板我見(jiàn)過(guò)有人硬背“先序切1:len(left)后序切:-1”之類的口訣一旦題目稍有變化就懵了。一定要理解其本質(zhì)——利用一種序列找根利用另一種序列中序的獨(dú)特性質(zhì)來(lái)劃分左右子樹(shù)邊界。抓住這個(gè)本質(zhì)無(wú)論題目怎么變你都能推導(dǎo)出正確的索引關(guān)系。二叉樹(shù)重建就像玩一個(gè)結(jié)構(gòu)拼圖遍歷序列就是給你的拼圖碎片和參考圖。掌握了“先序/后序定根中序分左右”這把萬(wàn)能鑰匙你就能從容地還原出任何一棵二叉樹(shù)的結(jié)構(gòu)骨架。希望這篇超詳講解能幫你把這把鑰匙牢牢握在手里。