日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

二叉树的几道相似简单递归题

發布時間:2023/12/4 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二叉树的几道相似简单递归题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

二叉樹中遞歸的思想,在這本Leetbook中講的很細了,這里不展開。下面是幾道例題:

226. 翻轉二叉樹(劍指 Offer 27. 二叉樹的鏡像)

遞歸法前序遍歷:

class Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:returnroot.left, root.right = root.right, root.left self.invertTree(root.left)self.invertTree(root.right)return root

迭代法前序遍歷:

class Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:return rootstack = []stack.append(root)while stack:node = stack.pop()if node:if node.right:stack.append(node.right)if node.left:stack.append(node.left)stack.append(node)stack.append(None)else:node = stack.pop()node.left, node.right = node.right, node.leftreturn root

遞歸法中序遍歷():

class Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:returnself.invertTree(root.left)root.left, root.right = root.right, root.left self.invertTree(root.left)return root

迭代法中序遍歷:

class Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:return rootstack = []stack.append(root)while stack:node = stack.pop()if node:if node.right:stack.append(node.right)stack.append(node)stack.append(None)if node.left:stack.append(node.left)else:node = stack.pop()node.left, node.right = node.right, node.leftreturn root

遞歸法后序遍歷:

class Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:returnleft = self.invertTree(root.left)right = self.invertTree(root.right)root.left, root.right = right, leftreturn root

迭代法后序遍歷:

class Solution:def invertTree(self, root: TreeNode) -> TreeNode:if not root:return rootstack = []stack.append(root)while stack:node = stack.pop()if node:stack.append(node)stack.append(None)if node.right:stack.append(node.right)if node.left:stack.append(node.left)else:node = stack.pop()node.left, node.right = node.right, node.leftreturn root

在遍歷的過程中,把記錄節點值這個操作改成交換左右子節點即可,前中后序遍歷都是可以的,遞歸法和迭代法都行。唯一要注意的就是中序遍歷,用遞歸法是左根左,因為使用遞歸的中序遍歷,某些節點的左右孩子會翻轉兩次。甚至,層序遍歷也是可以的,可以看我的這篇文章。

100. 相同的樹

class Solution:def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:if (not p) and (not q):return Trueelif (not p) or (not q):return Falseelif p.val != q.val:return Falseelse:return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

判斷兩個二叉樹是否相同,思路如下:兩個二叉樹是否同時為空?是否一個為空而另一個不為空?他們的根節點值是否相同?最后是遞歸的判斷,他們的左右子樹是否都相同?

101. 對稱二叉樹(劍指 Offer 28. 對稱的二叉樹)

class Solution:def isSymmetric(self, root: TreeNode) -> bool:return self.check(root, root)def check(self, p: TreeNode, q: TreeNode) -> bool:if (not p) and (not q):return Trueelif (not p) or (not q):return Falseelif p.val != q.val:return Falseelse:return self.check(p.left, q.right) and self.check(p.right, q.left)

判斷一個二叉樹是否為對稱二叉樹,可以轉化為判斷這個樹和自己的鏡像樹是否相同。關鍵在于用 self.check(root, root) 構造出兩個樹,然后以相反方向進行遞歸和判斷。

迭代寫法的話使用的是隊列,用棧或者數組等其他的容器都行,關鍵是把節點放入容器的順序不能錯,然后每次取兩個左右對應位置的節點出來做比較,如下:

class Solution:def isSymmetric(self, root: TreeNode) -> bool:if not root:return Truequeue = collections.deque()queue.append(root.left)queue.append(root.right)while queue:leftNode = queue.popleft()rightNode = queue.popleft()if (not leftNode) and (not rightNode):continueelif (not leftNode) or (not rightNode):return Falseelif leftNode.val != rightNode.val:return Falseelse:queue.append(leftNode.left)queue.append(rightNode.right)queue.append(leftNode.right)queue.append(rightNode.left)return True

617. 合并二叉樹

class Solution:def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:if not root1 and not root2:return Noneif not root1:return root2if not root2:return root1new_root = TreeNode(root1.val + root2.val) # 新建節點new_root.left = self.mergeTrees(root1.left, root2.left)new_root.right = self.mergeTrees(root1.right, root2.right)return new_root

兩個二叉樹一起從根節點開始向下遞歸,如果其中一個二叉樹的節點為空,則合并后的節點就是另一個二叉樹的節點。合并后的二叉樹每次都要新建節點,然后左右子樹用遞歸得到。優化的寫法是不新建節點,直接用其中一個二叉樹的節點,只需要把另一個二叉樹的節點值加過去即可。

class Solution:def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:if not root1 and not root2:return Noneif not root1:return root2if not root2:return root1root1.val += root2.val # 用舊節點root1.left = self.mergeTrees(root1.left, root2.left)root1.right = self.mergeTrees(root1.right, root2.right)return root1

104. 二叉樹的最大深度

class Solution:def maxDepth(self, root: TreeNode) -> int:if not root:return 0left_depth = self.maxDepth(root.left)right_depth = self.maxDepth(root.right)return max(left_depth, right_depth) + 1

自底向上的遞歸,在Leetbook中有詳細介紹,簡單來說:如果我們知道一個根節點,以其左子節點為根的最大深度為 left_depth 和以其右子節點為根的最大深度為 right_depth ,我們就可以選擇它們之間的最大值,再加上1來獲得根節點所在的子樹的最大深度。

543. 二叉樹的直徑

class Solution:def diameterOfBinaryTree(self, root: TreeNode) -> int:self.ans = 1def depth(node: TreeNode):if not node:return 0left_depth = depth(node.left)right_depth = depth(node.right)# 此節點作為根節點的最大深度是否為眾節點中最大,是則更新ansself.ans = max(self.ans, left_depth + right_depth + 1)return max(left_depth, right_depth) + 1depth(root)return self.ans - 1

雖然題目說明了直徑(最大的兩節點間路徑)不一定經過根節點,但是歸根到底,兩節點間路徑必然會有個根節點,目標就是找到把樹中所有節點都作為根節點時,各自求出最大深度(上一題思路),然后在這些最大深度中找到最大的作為直徑 ans

563. 二叉樹的坡度

class Solution:def findTilt(self, root: TreeNode) -> int:self.ans = 0def val_sum(node: TreeNode):if not node:return 0left_sum = val_sum(node.left)right_sum = val_sum(node.right)tilt = abs(left_sum - right_sum)self.ans += tiltreturn left_sum + right_sum + node.valval_sum(root)return self.ans

本題與最大深度類似,區別在于是記錄深度之差的絕對值(坡度),然后遞歸返回左右子樹和自身值之和。

112. 路徑總和

class Solution:def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:if not root: # 非節點return Falseif not root.left and not root.right: # 葉子節點return targetSum == root.valreturn self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)

思路是如果子樹的和等于 targetSum 減去當前節點的值,則存在路徑。所以終止條件為非節點則返回 False,為葉子節點則返回 targetSum == 當前節點的值,函數調用是子節點和減去當前節點值后的 targetSum

113. 路徑總和 II(劍指 Offer 34. 二叉樹中和為某一值的路徑)

class Solution:def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:ans = []path = []def dfs(root: TreeNode, targetSum: int):if not root:returnpath.append(root.val)targetSum -= root.valif not root.left and not root.right and targetSum == 0:ans.append(path.copy())dfs(root.left, targetSum)dfs(root.right, targetSum)path.pop()dfs(root, targetSum)return ans

這題不是求有沒有路徑,而是要把路徑都找出來,答案放在列表 ans 里。同樣還是遞歸地深度遍歷,用一個 path 列表記錄路徑,如果是葉節點且和數符合條件,就往 ans 里面加入路徑 path,否則就遍歷左子樹和右子樹,記得遞歸的最后要把 path 進行彈出,然后注意往 ans 里面加入的是 path 的淺拷貝,可以寫成 path.copy() 或者 path[:]

222. 完全二叉樹的節點個數

class Solution:def countNodes(self, root: TreeNode) -> int:if not root:return 0left = root.leftright = root.right# 深度初始化為 0,是因為位運算中 2 << 0 == 2,2 << 1 == 4leftHeight = 0rightHeight = 0# 求左子樹深度while left:left = left.leftleftHeight += 1# 求右子樹深度while right:right = right.rightrightHeight += 1# 若為滿二叉樹,則節點數為 2 的 leftHeight 次方,用位運算求if leftHeight == rightHeight:return (2 << leftHeight) - 1return self.countNodes(root.left) + self.countNodes(root.right) + 1

這題用常規的前中后序遍歷或者層序遍歷都能做,但是這樣就沒利用到完全二叉樹的性質了。完全二叉樹只有兩種情況,情況一:就是滿二叉樹,情況二:最后一層葉子節點沒有滿。

對于情況一,可以直接用 2^樹深度 - 1 來計算,注意這里根節點深度為1。

對于情況二,分別遞歸左孩子,和右孩子,遞歸到某一深度一定會有左孩子或者右孩子為滿二叉樹,然后依然可以按照情況1來計算。

優化點就是用位運算代替指數運算。

236. 二叉樹的最近公共祖先

class Solution:def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':def dfs(root: 'TreeNode', p: 'TreeNode', q: 'TreeNode'):# 如果當前節點為空,則說明 p、q 不在 node 的子樹中,不可能為公共祖先,直接返回 Noneif not root:return None# 如果當前節點 root 等于 p 或者 q,那么 root 就是 p、q 的最近公共祖先,直接返回 rootif root == p or root == q:return root# 遞歸遍歷左子樹、右子樹,并判斷左右子樹結果node_left = dfs(root.left, p, q)node_right = dfs(root.right, p, q)# 如果左右子樹都不為空,則說明 p、q 在當前根節點的兩側,當前根節點就是他們的最近公共祖先if node_left and node_right:return rootelif node_left:return node_left:else:return node_rightans = dfs(root, p, q)return ans

簡單概括,就是基于后序遍歷,從下往上,在找到 p 或者 q 的時候就返回。這樣當 p 和 q 在兩邊時,就會返回最近公共祖先;當 p 和 q 在同一條路徑上時,就會返回兩者中作為祖先的那個。

總結

以上是生活随笔為你收集整理的二叉树的几道相似简单递归题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。