下面是整理的一些二叉樹的常見問題,包括求最大最小深度、前中後序以及層序遍歷的遞歸和非遞歸解法、由前中序序列構建樹等。
1. 求最大深度
1.1 遞歸解法:非常清晰,遞歸邏輯即分別求左右子樹高度,取大值加一即可
def maxDepth(self, root):
if root is None:
return 0
lefth = self.maxDepth(root.left)
righth = self.maxDepth(root.right)
return max(lefth, righth) + 1
1.2 非遞歸解法:其實就是層序遍歷,用BFS和隊列的搭配使用可破。
def maxDepthBFS(self, root):
"""
bfs求最大深度,bfs與隊列黃金搭配
:param root:
:return:
"""
if root is None:
return 0
q = [root]
depth = 0
while q:
depth += 1
iter = len(q) # 當前隊列中的個數即爲該層的節點個數,需要記錄下來
for i in range(iter): # 然後一個一個pop出去,同時把子節點壓進去
cur_node = q.pop(0)
if cur_node.left is not None:
q.append(cur_node.left)
if cur_node.right is not None:
q.append(cur_node.right)
return depth
2. 求最小深度
遞歸解法跟最大深度類似,但要注意子樹爲空時的問題:在求最大深度時,子樹爲空不妨礙最大深度的值,但是最小深度時,若左子樹爲空,那麼最小深度只能來自於右子樹深度,而不是0。
下面代碼窮舉左右子樹是否爲空的四種情況,比較清晰
def minDepth(self, root):
if root is None:
return 0
# 兩個子樹都不空
if root.left is not None and root.right is not None:
return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
# 有一個子樹爲空,則取大值
elif root.left is None and root.right is None:
return self.minDepth(root.right) + 1
elif root.right is None and root.left is None:
return self.minDepth(root.left) + 1
# 兩個子樹都爲空
else:
return 1
3. 層序遍歷
3.1 遞歸解法:遞歸函數需要有一個參數level,該參數表示當前結點的層數。遍歷的結果返回到一個二維列表res=[[]]
中,res中的每一個子列表保存了對應index層的從左到右的所有結點value值。
def levelOrder_recur(self, root):
res =[[]]
def helper(node, level):
# 遞歸函數需要有一個參數level,該參數表示當前結點的層數。
if not node:
return
else:
res[level-1].append(node.val) # 加入到對應層中
if len(res) == level:
res.append([]) # 再擴展一行
helper(node.left, level+1)
helper(node.right, level+1)
helper(root, 1)
return res[:-1] # 不算最後一行,因爲前一層擴展了一個空行
3.2 非遞歸解法:求最大深度時已經涉及到,隊列中保存同一層的所有節點,通過同時壓出和壓入來體現同層的關係
def levelOrder(self, root):
"""
:param root:
:return: 層序遍歷,並返回層序的列表
"""
if root is None:
return
queue = []
queue.append(root)
result = []
depth = -1
while queue:
depth += 1
result.append([]) # 每多一層就擴展一行
# 記錄下當前隊列中節點的個數,即爲該層的節點個數
iter = len(queue)
for x in range(iter):
# 這個循環是指把同一層的節點全部pop出去,並且同時把下一層的節點push進來
node = queue.pop(0)
result[depth].append(node.val)
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
return result
擴展
Q:如果仍然按層遍歷,但是每層從右往左遍歷怎麼辦呢?
A:將上面的代碼left和right互換即可
Q:如果仍然按層遍歷,但是我要第一層從左往右,第二層從右往左,第三從左往右…這種zigzag遍歷方式如何實現?
A:將res[level-1].append(node.val)
進行一個層數奇偶的判斷,一個用append()
,一個用insert(0,)
4. 前序遍歷
4.1 遞歸解法:中左右的順序,保存到列表中,注意兩個列表的相連是+
號
def preorder_recur(self, root):
if root is None:
return []
return [root.val] + self.preorder(root.left) + self.preorder(root.right)
4.2 非遞歸解法:用res保存結果,用cur代表當前節點,用stack來存放當前節點的右子節點,然後按照一直往左延伸下去
def preorder(self, root):
stack = []
res = []
cur = root
while stack or cur:
if cur:
res.append(cur.val)
stack.append(cur.right)
cur = cur.left
else:
cur = stack.pop() # 取出保存的右子節點
return res
5. 中序遍歷
5.1 遞歸解法
def inorder_recur(self, root):
if root is None:
return []
return self.inorder_recur(root.left) + [root.val] + self.inorder_recur(root.right)
5.2 非遞歸:保存當前節點到棧中,從棧取出同時加入結果,並移到右子樹
def inorder(self, root):
stack = []
res = []
cur = root
while stack or cur:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
6. 後序遍歷
6.1 遞歸
def postorder_recur(self, root):
if not root:
return []
return self.postorder_recur(root.left) + self.postorder_recur(root.right) + [root.val]
6.2 非遞歸:左右中的順序,倒過來是中右左的順序,跟前序遍歷只是左右相反,因此可以把前序遍歷的左右交換,最後逆序輸出列表,即爲答案
def postorder(self,root):
"""
左右中 是中右左的逆序 只要把preorder左右互換即可
:param root:
:return:
"""
stack = []
res = []
cur = root
while stack or cur:
if cur:
res.append(cur.val)
stack.append(cur.left)
cur = cur.right
else:
cur = stack.pop()
return res[::-1]
7. 根據前序、中序序列構建樹/求出後序序列
通過前序序列獲取根節點,在中序序列中根據根節點劃分爲左右子樹
def getTreePreMid(self, pre, mid):
"""
前序 中序 構建樹
:param pre:
:param mid:
:return: root
"""
if len(pre) == 0:
return None
if len(pre) == 1:
return TreeNode(pre[0])
root = pre[0] # 前序序列第一個值就是根節點
root_idx = mid.index(root) # 找到該值在中序序列中的位置
root.left = self.getTreePreMid(pre[1:1+root_idx], mid[0:root_idx]) # 左右兩部分分別遞歸求解
root.right = self.getTreePreMid(pre[1+root_idx:], mid[root_idx+1:])
return root