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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

LeetCode Hot100 ---- 二叉树专题

發(fā)布時(shí)間:2023/12/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LeetCode Hot100 ---- 二叉树专题 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

樹(shù)

力扣102:二叉搜索樹(shù)的層次遍歷

力扣105:從前序和中序重構(gòu)二叉樹(shù)

力扣108:將有序數(shù)組轉(zhuǎn)化為二叉搜索樹(shù)

力扣110:平衡二叉樹(shù)

力扣113:路徑總和

力扣124:二叉樹(shù)的最大路徑和

力扣1325:刪除給定值的葉子節(jié)點(diǎn)

力扣144:二叉樹(shù)的前序遍歷(非遞歸)

力扣145:二叉樹(shù)的后續(xù)遍歷(非遞歸)

力扣199:二叉樹(shù)的右視圖

力扣208:實(shí)現(xiàn)Trie前綴樹(shù)

力扣222:完全二叉樹(shù)的節(jié)點(diǎn)數(shù)

力扣226:翻轉(zhuǎn)二叉樹(shù)

力扣236:二叉樹(shù)的最近公共祖先

力扣257:二叉樹(shù)的所有路徑

力扣297:二叉樹(shù)的序列化和反序列化

力扣450:刪除二叉樹(shù)中的節(jié)點(diǎn)

力扣543:二叉樹(shù)的直徑長(zhǎng)度

力扣617:合并二叉樹(shù)

力扣662:二叉樹(shù)的最大寬度

力扣687:最長(zhǎng)同值路徑

力扣94:二叉樹(shù)中序遍歷(非遞歸)

力扣958:二叉樹(shù)的完全性檢驗(yàn)

力扣98:驗(yàn)證二叉搜索樹(shù)

力扣99:恢復(fù)二叉搜索樹(shù)

重建二叉樹(shù)

Z字形層次遍歷

知識(shí)點(diǎn)

  • 掌握二叉樹(shù)遞歸與非遞歸遍歷
  • 理解 DFS 前序遍歷與分治法
  • 理解 BFS 層次遍歷

二叉樹(shù)遍歷

  • 前序遍歷先訪問(wèn)根節(jié)點(diǎn),再前序遍歷左子樹(shù),再前序遍歷右子樹(shù)
  • 中序遍歷:先中序遍歷左子樹(shù),再訪問(wèn)根節(jié)點(diǎn),再中序遍歷右子樹(shù)
  • 后序遍歷:先后序遍歷左子樹(shù),再后序遍歷右子樹(shù),再訪問(wèn)根節(jié)點(diǎn)

注意點(diǎn)

  • 以根訪問(wèn)順序決定是什么遍歷
  • 左子樹(shù)都是優(yōu)先右子樹(shù)

遞歸模板

  • 遞歸實(shí)現(xiàn)二叉樹(shù)遍歷非常簡(jiǎn)單,不同順序區(qū)別僅在于訪問(wèn)父結(jié)點(diǎn)順序
def preorder_rec(root):if root is None:returnvisit(root)preorder_rec(root.left)preorder_rec(root.right)returndef inorder_rec(root):if root is None:returninorder_rec(root.left)visit(root)inorder_rec(root.right)returndef postorder_rec(root):if root is None:returnpostorder_rec(root.left)postorder_rec(root.right)visit(root)return

前序非遞歸

  • 本質(zhì)上是圖的DFS的一個(gè)特例,因此可以用棧來(lái)實(shí)現(xiàn)
class Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:if not root: return []res, stack = [], [root] # 利用棧進(jìn)行臨時(shí)存儲(chǔ)while stack:node = stack.pop() # 取出一個(gè)節(jié)點(diǎn),表示開(kāi)始訪問(wèn)以該節(jié)點(diǎn)為根的子樹(shù)res.append(node.val) # 首先訪問(wèn)該節(jié)點(diǎn)(先序),之后順序入棧右子樹(shù),左子樹(shù)if node.right:stack.append(node.right)if node.left:stack.append(node.left)return res

中序非遞歸

中序遍歷的思想是:
1.若節(jié)點(diǎn)還有左子樹(shù),就要把左子樹(shù)訪問(wèn)完;
2.沒(méi)有左子樹(shù)可以訪問(wèn)時(shí),訪問(wèn)該節(jié)點(diǎn),并嘗試訪問(wèn)右子樹(shù)

class Solution:def inorderTraversal(self, root: TreeNode) -> List[int]:s, inorder = [], []node = rootwhile len(s) > 0 or node is not None:if node is not None:s.append(node)node = node.leftelse:node = s.pop()inorder.append(node.val)node = node.rightreturn inorder

后序非遞歸

后序遍歷(迭代法)

再來(lái)看后序遍歷,先序遍歷是中左右,后續(xù)遍歷是左右中,那么我們只需要調(diào)整一下先序遍歷的代碼順序,就變成中右左的遍歷順序,然后在反轉(zhuǎn)result數(shù)組,輸出的結(jié)果順序就是左右中了,如下圖:

所以后序遍歷只需要前序遍歷的代碼稍作修改就可以了,代碼如下:

# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution:def postorderTraversal(self, root):preorder = []if root is None:return preorders = [root]while len(s) > 0:node = s.pop()preorder.append(node.val)if node.left is not None:s.append(node.left)if node.right is not None:s.append(node.right)return preorder[::-1] # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution:def postorderTraversal(self, root: TreeNode) -> List[int]:stack, node = [], rootprev = Noneres = []if not root: return [] # 基本情況直接返回while node or stack:while node: # 走到左分支的盡頭stack.append(node)node = node.leftnode = stack.pop()if not node.right or node.right == prev: # 如果左分支的盡頭沒(méi)有右子節(jié)點(diǎn),就應(yīng)該把它放進(jìn)去res.append(node.val)prev = node # 記錄母節(jié)點(diǎn)node = Noneelse:stack.append(node) # 如果有右子節(jié)點(diǎn),放到stack里面,有點(diǎn)像dfsnode = node.rightreturn res

后續(xù)遍歷的主要特點(diǎn)是:遍歷左分支的左右根節(jié)點(diǎn),再把左分支的主干節(jié)點(diǎn)逐個(gè)返回。

注意點(diǎn)

  • 核心就是:根節(jié)點(diǎn)必須在右節(jié)點(diǎn)彈出之后,再?gòu)棾?/li>
class Solution:def postorderTraversal(self, root: TreeNode) -> List[int]:s, postorder = [], []node, last_visit = root, Nonewhile len(s) > 0 or node is not None:if node is not None:s.append(node)node = node.leftelse:peek = s[-1]if peek.right is not None and last_visit != peek.right:node = peek.rightelse:last_visit = s.pop()postorder.append(last_visit.val)return postorder

分治法應(yīng)用

先分別處理局部,再合并結(jié)果

適用場(chǎng)景

  • 快速排序
  • 歸并排序
  • 二叉樹(shù)相關(guān)問(wèn)題

分治法模板

  • 遞歸返回條件
  • 分段處理
  • 合并結(jié)果

104. 二叉樹(shù)的最大深度

給定一個(gè)二叉樹(shù),找出其最大深度。

class Solution:def maxDepth(self, root: TreeNode) -> int:if not root:return 0return 1 + max([self.maxDepth(root.left), self.maxDepth(root.right)])
  • 思路 2:層序遍歷
class Solution:def maxDepth(self, root: TreeNode) -> List[List[int]]:depth = 0if root is None:return depthbfs = collections.deque([root])while len(bfs) > 0:depth += 1level_size = len(bfs)for _ in range(level_size):node = bfs.popleft()if node.left is not None:bfs.append(node.left)if node.right is not None:bfs.append(node.right)return depth

94. 二叉樹(shù)的中序遍歷

給定一個(gè)二叉樹(shù)的根節(jié)點(diǎn)root,返回它的中序 遍歷。

示例 1:

1\2/ 3 輸出:[1,3,2]

思路:經(jīng)典遞歸.

class Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:if root is None:return []left_result = self.preorderTraversal(root.left)right_result = self.preorderTraversal(root.right)return [root.val] + left_result + right_result class Solution:def inorderTraversal(self, root: TreeNode) -> List[int]:if not root:return []return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)

98. 驗(yàn)證二叉搜索樹(shù)

給定一個(gè)二叉樹(shù),判斷其是否是一個(gè)有效的二叉搜索樹(shù)。

思路:

  • 得到中序遍歷的結(jié)果;
  • 判斷中序遍歷是否單調(diào)遞增。
  • class Solution:def isValidBST(self, root: TreeNode) -> bool:def get_mfs(root):if not root:return []return get_mfs(root.left) +[root.val] + get_mfs(root.right)?res = get_mfs(root) if len(res) <= 1:return Truefor i in range(1,len(res)):if res[i] <= res[i-1]:return Falsereturn True

    分治法: 一個(gè)二叉樹(shù)為合法的二叉搜索樹(shù)當(dāng)且僅當(dāng)左右子樹(shù)為合法二叉搜索樹(shù)且根結(jié)點(diǎn)值大于右子樹(shù)最小值小于左子樹(shù)最大值。

    # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution:def isValidBST(self, root):if root is None: return Truedef valid_min_max(node):isValid = Trueif node.left is not None:l_isValid, l_min, l_max = valid_min_max(node.left)isValid = isValid and node.val > l_maxelse:l_isValid, l_min = True, node.valif node.right is not None:r_isValid, r_min, r_max = valid_min_max(node.right)isValid = isValid and node.val < r_minelse:r_isValid, r_max = True, node.valreturn l_isValid and r_isValid and isValid, l_min, r_maxreturn valid_min_max(root)[0] class Solution:def isValidBST(self, root: TreeNode) -> bool:if root is None:return Trues = [(root, float('-inf'), float('inf'))]while len(s) > 0:node, low, up = s.pop()if node.left is not None:if node.left.val <= low or node.left.val >= node.val:return Falses.append((node.left, low, node.val))if node.right is not None:if node.right.val <= node.val or node.right.val >= up:return Falses.append((node.right, node.val, up))return True

    543. 二叉樹(shù)的直徑

    給定一棵二叉樹(shù),你需要計(jì)算它的直徑長(zhǎng)度。一棵二叉樹(shù)的直徑長(zhǎng)度是任意兩個(gè)結(jié)點(diǎn)路徑長(zhǎng)度中的最大值。這條路徑可能穿過(guò)也可能不穿過(guò)根結(jié)點(diǎn)。

    class Solution:def diameterOfBinaryTree(self, root: TreeNode) -> int:self.ans = 1def depth(node):# 訪問(wèn)到空節(jié)點(diǎn)了,返回0if not node:return 0# 左兒子為根的子樹(shù)的深度L = depth(node.left)# 右兒子為根的子樹(shù)的深度R = depth(node.right)# 計(jì)算d_node即L+R+1 并更新ansself.ans = max(self.ans, L + R + 1)# 返回該節(jié)點(diǎn)為根的子樹(shù)的深度return max(L, R) + 1depth(root)return self.ans - 1

    102. 二叉樹(shù)的層序遍歷

    給你一個(gè)二叉樹(shù),請(qǐng)你返回其按 層序遍歷 得到的節(jié)點(diǎn)值。 (即逐層地,從左到右訪問(wèn)所有節(jié)點(diǎn))。

    示例:

    3/ \9 20/ \15 7

    返回其層序遍歷結(jié)果:

    [[3],[9,20],[15,7]]

    思路:二叉樹(shù)的題目很多適合用遞歸,因?yàn)槎鏄?shù)本身就是由相同的節(jié)點(diǎn)結(jié)構(gòu)組成。而此題用迭代實(shí)現(xiàn)更直觀。

    # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution:def levelOrder(self, root: TreeNode) -> List[List[int]]:if not root: return []#跟結(jié)點(diǎn)入queuequeue = [root]res = []while queue:res.append([node.val for node in queue])#存儲(chǔ)當(dāng)前層的孩子節(jié)點(diǎn)列表ll = []#對(duì)當(dāng)前層的每個(gè)節(jié)點(diǎn)遍歷for node in queue:#如果左子節(jié)點(diǎn)存在,入隊(duì)列if node.left:ll.append(node.left)#如果右子節(jié)點(diǎn)存在,入隊(duì)列if node.right:ll.append(node.right)#后把queue更新成下一層的結(jié)點(diǎn),繼續(xù)遍歷下一層queue = llreturn res

    對(duì)于題目199,右視圖即每層最右邊的值,因此只需要做一個(gè)非常簡(jiǎn)單的小變化,即返回的列表只需要對(duì)每一層的列表的最后一個(gè)元素入結(jié)果列表就可以了

    #只需要對(duì)該層最后一個(gè)元素入列表 res.append([node.val for node in queue][-1]) # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution:def rightSideView(self, root: TreeNode) -> List[int]:if not root: return []#跟結(jié)點(diǎn)入queuequeue = [root]res = []while queue:#只需要對(duì)該層最后一個(gè)元素入列表res.append([node.val for node in queue][-1])#存儲(chǔ)當(dāng)前層的孩子節(jié)點(diǎn)列表ll = []#對(duì)當(dāng)前層的每個(gè)節(jié)點(diǎn)遍歷for node in queue:#如果左子節(jié)點(diǎn)存在,入隊(duì)列if node.left:ll.append(node.left)#如果右子節(jié)點(diǎn)存在,入隊(duì)列if node.right:ll.append(node.right)#后把queue更新成下一層的結(jié)點(diǎn),繼續(xù)遍歷下一層queue = llreturn res

    insert-into-a-binary-search-tree

    給定二叉搜索樹(shù)(BST)的根節(jié)點(diǎn)和要插入樹(shù)中的值,將值插入二叉搜索樹(shù)。 返回插入后二叉搜索樹(shù)的根節(jié)點(diǎn)。 保證原始二叉搜索樹(shù)中不存在新值。

    class Solution:def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:if root is None:return TreeNode(val)if val > root.val:root.right = self.insertIntoBST(root.right, val)else:root.left = self.insertIntoBST(root.left, val)return root

    437. 路徑總和 III

    給定一個(gè)二叉樹(shù),它的每個(gè)結(jié)點(diǎn)都存放著一個(gè)整數(shù)值。

    找出路徑和等于給定數(shù)值的路徑總數(shù)。

    路徑不需要從根節(jié)點(diǎn)開(kāi)始,也不需要在葉子節(jié)點(diǎn)結(jié)束,但是路徑方向必須是向下的(只能從父節(jié)點(diǎn)到子節(jié)點(diǎn))。

    基本思路:先回溯得到以根節(jié)點(diǎn)為起點(diǎn)(一定包含根節(jié)點(diǎn))的路徑和滿足要求的總數(shù)。再深度優(yōu)先搜索,即根節(jié)點(diǎn)路徑數(shù)目+左節(jié)點(diǎn)路徑數(shù)目+右節(jié)點(diǎn)路徑數(shù)目

    class Solution:def pathSum(self, root: TreeNode, targetSum: int) -> int:def backtrace(root, sum):res = 0if not root:return resif sum == root.val:res += 1if root.left:res += backtrace(root.left, sum - root.val)if root.right:res += backtrace(root.right, sum - root.val)return res# 返回路徑和等于數(shù)值的路徑數(shù)def DFS(root):if not root:return 0root_count = backtrace(root, targetSum)left_root = DFS(root.left)right_root = DFS(root.right)return left_root+right_root+root_countreturn DFS(root)

    617. 合并二叉樹(shù)

    給定兩個(gè)二叉樹(shù),想象當(dāng)你將它們中的一個(gè)覆蓋到另一個(gè)上時(shí),兩個(gè)二叉樹(shù)的一些節(jié)點(diǎn)便會(huì)重疊。

    你需要將他們合并為一個(gè)新的二叉樹(shù)。合并的規(guī)則是如果兩個(gè)節(jié)點(diǎn)重疊,那么將他們的值相加作為節(jié)點(diǎn)合并后的新值,否則不為 NULL 的節(jié)點(diǎn)將直接作為新二叉樹(shù)的節(jié)點(diǎn)。

    示例 1:

    輸入: Tree 1 Tree 2 1 2 / \ / \ 3 2 1 3 / \ \ 5 4 7 輸出: 合并后的樹(shù):3/ \4 5/ \ \ 5 4 7 注意: 合并必須從兩個(gè)樹(shù)的根節(jié)點(diǎn)開(kāi)始。

    思路:經(jīng)典左右遞歸

    class Solution:def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:if not t2:return t1elif not t1:return t2else:t3 = TreeNode()t3.val = t1.val + t2.valt3.left = self.mergeTrees(t1.left,t2.left)t3.right = self.mergeTrees(t1.right,t2.right)return t3

    226. 翻轉(zhuǎn)二叉樹(shù)

    左右翻轉(zhuǎn)一棵二叉樹(shù)。

    思路:經(jīng)典左右遞歸

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

    114. 二叉樹(shù)展開(kāi)為鏈表

    給定一個(gè)二叉樹(shù),原地將它展開(kāi)為一個(gè)單鏈表。

    1/ \2 5/ \ \ 3 4 6

    將其展開(kāi)為:

    1\2\3\4\5\6

    思路:前序遍歷。

    class Solution:def flatten(self, root: TreeNode) -> None:if not root:return# 先把左右兩個(gè)子樹(shù)處理成鏈表self.flatten(root.left)self.flatten(root.right)# 接下來(lái)把右邊鏈表剝離,把左邊鏈表掛在root.righttmp_right = root.rightroot.right = root.leftroot.left = None# 然后把右邊鏈表掛在原左邊鏈表的最后while root.right!=None:root = root.right # 找到原左鏈表的末尾,用于接右鏈表root.right = tmp_right

    96. 不同的二叉搜索樹(shù)

    給定一個(gè)整數(shù)n,求以 1 ... n 為節(jié)點(diǎn)組成的二叉搜索樹(shù)有多少種?

    思路:

    class Solution:def numTrees(self, n):""":type n: int:rtype: int"""G = [0]*(n+1)G[0], G[1] = 1, 1for i in range(2, n+1):for j in range(1, i+1):G[i] += G[j-1] * G[i-j]return G[n]

    時(shí)間復(fù)雜度 : O(n^2),其中 n表示二叉搜索樹(shù)的節(jié)點(diǎn)個(gè)數(shù)。G(n)函數(shù)一共有 n 個(gè)值需要求解,每次求解需要 O(n) 的時(shí)間復(fù)雜度,因此總時(shí)間復(fù)雜度為 O(n^2)

    空間復(fù)雜度 : O(n)。我們需要 O(n)的空間存儲(chǔ) G 數(shù)組。

    105. 從前序與中序遍歷序列構(gòu)造二叉樹(shù)

    根據(jù)一棵樹(shù)的前序遍歷與中序遍歷構(gòu)造二叉樹(shù), 樹(shù)中沒(méi)有重復(fù)的元素。

    例如:

    前序遍歷 preorder = [3,9,20,15,7] 中序遍歷 inorder = [9,3,15,20,7] 返回:3/ \9 20/ \15 7

    思路:前序遍歷[根節(jié)點(diǎn),左子樹(shù),右子樹(shù)],中序遍歷[左子樹(shù),根節(jié)點(diǎn),右子樹(shù)],顯然前序遍歷第一個(gè)為根節(jié)點(diǎn),根據(jù)根節(jié)點(diǎn)在中序遍歷中定位左右子樹(shù)的中序遍歷,同時(shí)可知左右子樹(shù)的元素?cái)?shù)量,由此在前序遍歷中找到左右子樹(shù)的前序遍歷。由此遞歸。

    class Solution:def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:if not preorder:returnroot = TreeNode()root.val = preorder[0]index = inorder.index(root.val)root.left = self.buildTree(preorder[1:index+1],inorder[:index])root.right = self.buildTree(preorder[index+1:],inorder[index+1:])return root

    236. 二叉樹(shù)的最近公共祖先

    給定一個(gè)二叉樹(shù), 找到該樹(shù)中兩個(gè)指定節(jié)點(diǎn)的最近公共祖先。

    所有節(jié)點(diǎn)的值都是唯一的。
    p、q 為不同節(jié)點(diǎn)且均存在于給定的二叉樹(shù)中。

    思路:

    某節(jié)點(diǎn)是公共祖先有兩種情況:

  • 該節(jié)點(diǎn)為指定節(jié)點(diǎn)中的一個(gè),且子樹(shù)中存在另一個(gè)節(jié)點(diǎn)。
  • 該節(jié)點(diǎn)不是指定節(jié)點(diǎn),但其左右子樹(shù)均存在指定節(jié)點(diǎn)。
  • class Solution:def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':if not root:returnif root==q or root==p:return root # 可能為情況1,也可能是情況1中的子樹(shù)left = self.lowestCommonAncestor(root.left,p,q)right = self.lowestCommonAncestor(root.right,p,q)if left and right:return root # 情況2if not left:return rightif not right:return left

    538. 把二叉搜索樹(shù)轉(zhuǎn)換為累加樹(shù)

    給出二叉搜索樹(shù)的根節(jié)點(diǎn),該樹(shù)的節(jié)點(diǎn)值各不相同,請(qǐng)你將其轉(zhuǎn)換為累加樹(shù)(Greater Sum Tree),使每個(gè)節(jié)點(diǎn) node 的新值等于原樹(shù)中大于或等于node.val的值之和。

    思路:

    本題中要求我們將每個(gè)節(jié)點(diǎn)的值修改為原來(lái)的節(jié)點(diǎn)值加上所有大于它的節(jié)點(diǎn)值之和。這樣我們只需要反序中序遍歷該二叉搜索樹(shù),記錄過(guò)程中的節(jié)點(diǎn)值之和,并不斷更新當(dāng)前遍歷到的節(jié)點(diǎn)的節(jié)點(diǎn)值,即可得到題目要求的累加樹(shù)。

    其實(shí)這就是一棵樹(shù),大家可能看起來(lái)有點(diǎn)別扭,換一個(gè)角度來(lái)看,這就是一個(gè)有序數(shù)組[2, 5, 13],求從后到前的累加數(shù)組,也就是[20, 18, 13],大家是不是感覺(jué)這就是送分題了。

    為什么變成數(shù)組就是送分題了呢,因?yàn)閿?shù)組大家都知道怎么遍歷啊,從后向前,挨個(gè)累加就完事了,這換成了二叉搜索樹(shù),看起來(lái)就別扭了一些是不是。

    那么知道如何遍歷這個(gè)二叉樹(shù),也就迎刃而解了,從樹(shù)中可以看出累加的順訊是 右中左,所以我們需要中序遍歷反過(guò)來(lái)遍歷這個(gè)二叉樹(shù),然后順序累加就可以了。

    class Solution:def __init__(self):self.accum = 0def convertBST(self, root: TreeNode) -> TreeNode:if not root:return # 接下來(lái)逆中序遍歷rootself.convertBST(root.right) # 1. 遍歷右子樹(shù)root.val += self.accum # 2. 遍歷當(dāng)前結(jié)點(diǎn)self.accum = root.val self.convertBST(root.left) # 3. 遍歷左子樹(shù)return root

    337. 打家劫舍 III

    二叉樹(shù)的每個(gè)節(jié)點(diǎn)數(shù)字代表屋子中的金額,如果兩個(gè)直接相連的房子在同一天晚上被打劫,房屋將自動(dòng)報(bào)警。計(jì)算在不觸動(dòng)警報(bào)的情況下,小偷一晚能夠盜取的最高金額。

    示例:

    輸入: [3,4,5,1,3,null,1]3/ \4 5/ \ \ 1 3 1輸出: 9 解釋: 小偷一晚能夠盜取的最高金額 = 4 + 5 = 9.

    思路:

    這道題目和 198.打家劫舍,213.打家劫舍II 如出一轍,只不過(guò)這個(gè)換成了樹(shù)。

    如果對(duì)樹(shù)的遍歷不夠熟悉的話,那本題就有難度了。

    對(duì)于樹(shù)的話,首先就要想到遍歷方式,前中后序(深度優(yōu)先搜索)還是層序遍歷(廣度優(yōu)先搜索)。

    本題一定是要后序遍歷,因?yàn)橥ㄟ^(guò)遞歸函數(shù)的返回值來(lái)做下一步計(jì)算。

    與198.打家劫舍,213.打家劫舍II 一樣,關(guān)鍵是要討論當(dāng)前節(jié)點(diǎn)搶還是不搶。

    如果搶了當(dāng)前節(jié)點(diǎn),兩個(gè)孩子就不是動(dòng),如果沒(méi)搶當(dāng)前節(jié)點(diǎn),就可以考慮搶左右孩子(注意這里說(shuō)的是“考慮”)

    動(dòng)態(tài)規(guī)劃其實(shí)就是使用狀態(tài)轉(zhuǎn)移容器來(lái)記錄狀態(tài)的變化,這里可以使用一個(gè)長(zhǎng)度為2的數(shù)組,記錄當(dāng)前節(jié)點(diǎn)偷與不偷所得到的的最大金錢(qián)。

    1.確定遞歸函數(shù)的參數(shù)和返回值

    這里我們要求一個(gè)節(jié)點(diǎn) 偷與不偷的兩個(gè)狀態(tài)所得到的金錢(qián),那么返回值就是一個(gè)長(zhǎng)度為2的數(shù)組。

    ??????? def rob_tree(root):
    ??????????? '''
    ??????????? 返回一個(gè)元組(rob, skip),分別代表偷root節(jié)點(diǎn)的總收益,和不偷的總收益。
    ??????????? 總收益的意思是以root為根節(jié)點(diǎn)的子樹(shù)的總收益。
    ??????????? '''

    所以dp數(shù)組(dp table)以及下標(biāo)的含義:下標(biāo)為0記錄不偷該節(jié)點(diǎn)所得到的的最大金錢(qián),下標(biāo)為1記錄偷該節(jié)點(diǎn)所得到的的最大金錢(qián)。所以本題dp數(shù)組就是一個(gè)長(zhǎng)度為2的數(shù)組!

    那么有同學(xué)可能疑惑,長(zhǎng)度為2的數(shù)組怎么標(biāo)記樹(shù)中每個(gè)節(jié)點(diǎn)的狀態(tài)呢?

    別忘了在遞歸的過(guò)程中,系統(tǒng)棧會(huì)保存每一層遞歸的參數(shù)。

    如果還不理解的話,就接著往下看,看到代碼就理解了哈。

    2 確定終止條件

    在遍歷的過(guò)程中,如果遇到空間點(diǎn)的話,很明顯,無(wú)論偷還是不偷都是0,所以就返回
    ??????????? if not root:
    ??????????????? return (0,0) # (偷當(dāng)前節(jié)點(diǎn)金額,不偷當(dāng)前節(jié)點(diǎn)金額)

    這也相當(dāng)于dp數(shù)組的初始化

    3.確定遍歷順序

    首先明確的是使用后序遍歷。 因?yàn)橥ㄟ^(guò)遞歸函數(shù)的返回值來(lái)做下一步計(jì)算。

    a.通過(guò)遞歸左節(jié)點(diǎn),得到左節(jié)點(diǎn)偷與不偷的金錢(qián)。

    b.通過(guò)遞歸右節(jié)點(diǎn),得到右節(jié)點(diǎn)偷與不偷的金錢(qián)。

    ??????????? left_subtree = rob_tree(root.left)
    ??????????? right_subtree = rob_tree(root.right)

    ?4. 確定單層遞歸的邏輯

    如果是偷當(dāng)前節(jié)點(diǎn),那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; (如果對(duì)下標(biāo)含義不理解就在回顧一下dp數(shù)組的含義)

    如果不偷當(dāng)前節(jié)點(diǎn),那么左右孩子就可以偷,至于到底偷不偷一定是選一個(gè)最大的,所以:val2 = max(left[0], left[1]) + max(right[0], right[1]);

    最后當(dāng)前節(jié)點(diǎn)的狀態(tài)就是{val2, val1}; 即:{不偷當(dāng)前節(jié)點(diǎn)得到的最大金錢(qián),偷當(dāng)前節(jié)點(diǎn)得到的最大金錢(qián)}

    對(duì)于每個(gè)節(jié)點(diǎn),有兩種狀態(tài):盜竊該節(jié)點(diǎn)和不盜竊。如果盜竊,則其左右子節(jié)點(diǎn)不能盜竊;如果不盜竊,則左右子節(jié)點(diǎn)也可以取兩種狀態(tài)。

    最后頭結(jié)點(diǎn)就是 取下標(biāo)0 和 下標(biāo)1的最大值就是偷得的最大金錢(qián)

    class Solution:def rob(self, root: TreeNode) -> int:def rob_tree(root):'''返回一個(gè)元組(rob, skip),分別代表偷root節(jié)點(diǎn)的總收益,和不偷的總收益。總收益的意思是以root為根節(jié)點(diǎn)的子樹(shù)的總收益。'''if not root:return (0,0) # (偷當(dāng)前節(jié)點(diǎn)金額,不偷當(dāng)前節(jié)點(diǎn)金額)# 考慮以root為根節(jié)點(diǎn)的子樹(shù)的收益, 偷了root就不能再偷左右子節(jié)點(diǎn)了left_subtree = rob_tree(root.left)right_subtree = rob_tree(root.right)rob = root.val + left_subtree[1] +right_subtree[1] # 偷當(dāng)前節(jié)點(diǎn),不能偷子節(jié)點(diǎn)skip = max(left_subtree[0], left_subtree[1]) + max(right_subtree[0], right_subtree[1]) # 不偷當(dāng)前節(jié)點(diǎn),可偷可不偷子節(jié)點(diǎn)return rob, skipresult = self.rob_tree(root)return max(result[0], result[1])

    101. 對(duì)稱二叉樹(shù)

    給定一個(gè)二叉樹(shù),檢查它是否是鏡像對(duì)稱的。

    思路:

    遞歸思路:

    class Solution:def isSymmetric(self, root: TreeNode) -> bool:def is_sym(a,b):if not a and not b:return Trueelif not a or not b:return Falseif a.val != b.val:return Falsereturn is_sym(a.left,b.right) and is_sym(a.right,b.left)if not root:return Truereturn is_sym(root.left,root.right)

    迭代思路,一層一層遍歷,每層查看是否對(duì)稱:

    class Solution:def isSymmetric(self, root: TreeNode) -> bool:if not root:return Truelayer = [root]while layer:vals = [root.val if root else None for root in layer ]if vals!=vals[::-1]:return Falselayer = [(root.left, root.right) for root in layer if root]layer = [i for tpl in layer for i in tpl]return True

    297. 二叉樹(shù)的序列化與反序列化

    將二叉樹(shù)轉(zhuǎn)化為字符串,并且可以將字符串再轉(zhuǎn)化為原始的二叉樹(shù)。

    思路

    序列化的就是把數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為字符串,可以使用DFS的方式,二叉樹(shù)DFS的方法就是對(duì)每個(gè)節(jié)點(diǎn),都先處理左子節(jié)點(diǎn),再處理右子節(jié)點(diǎn)。

    class Codec:def serialize(self, root):vals = []def dfs(node):if node:vals.append(str(node.val))dfs(node.left)dfs(node.right)else:vals.append("#")dfs(root)s = " ".join(vals)return sdef deserialize(self, data):def redfs():if vals:val = vals.pop(0)else:returnif val == "#":return Nonenode = TreeNode(int(val))node.left = redfs()node.right = redfs()return nodevals = data.split(" ")return redfs()

    124. 二叉樹(shù)中的最大路徑和

    路徑被定義為一條從樹(shù)中任意節(jié)點(diǎn)出發(fā),沿父節(jié)點(diǎn)-子節(jié)點(diǎn)連接,達(dá)到任意節(jié)點(diǎn)的序列。該路徑 至少包含一個(gè) 節(jié)點(diǎn),且不一定經(jīng)過(guò)根節(jié)點(diǎn)。

    路徑和 是路徑中各節(jié)點(diǎn)值的總和。

    給你一個(gè)二叉樹(shù)的根節(jié)點(diǎn) root ,返回其 最大路徑和 。

    思路:

    遞歸,對(duì)每個(gè)節(jié)點(diǎn),最大值有四種情況:

  • 僅該節(jié)點(diǎn)(比如左右子樹(shù)都是負(fù)數(shù))
  • 該節(jié)點(diǎn)加左子樹(shù)單鏈最大值
  • 該節(jié)點(diǎn)加右子樹(shù)單鏈的最大值
  • 該節(jié)點(diǎn)加左右子樹(shù)單鏈的最大值
  • class Solution:def recur(self,root):if not root:return 0left = self.recur(root.left)right = self.recur(root.right)tmpv = max(left+root.val, right+root.val, left + root.val + right, root.val)if tmpv > self.maxv:self.maxv = tmpvreturn max(left, right, 0) + root.valdef maxPathSum(self, root):self.maxv = root.val_ = self.recur(root)return self.maxv class Solution:def __init__(self):self.maxSum = float("-inf")def maxPathSum(self, root):def maxGain(node):if not node:return 0# 遞歸計(jì)算左右子節(jié)點(diǎn)的最大貢獻(xiàn)值# 只有在最大貢獻(xiàn)值大于 0 時(shí),才會(huì)選取對(duì)應(yīng)子節(jié)點(diǎn)leftGain = max(maxGain(node.left), 0)rightGain = max(maxGain(node.right), 0)# 節(jié)點(diǎn)的最大路徑和取決于該節(jié)點(diǎn)的值與該節(jié)點(diǎn)的左右子節(jié)點(diǎn)的最大貢獻(xiàn)值priceNewpath = node.val + leftGain + rightGain# 更新答案self.maxSum = max(self.maxSum, priceNewpath)# 返回節(jié)點(diǎn)的最大貢獻(xiàn)值return node.val + max(leftGain, rightGain)maxGain(root)return self.maxSum

    balanced-binary-tree

    給定一個(gè)二叉樹(shù),判斷它是否是高度平衡的二叉樹(shù)。

    • 思路 1:分治法,左邊平衡 && 右邊平衡 && 左右兩邊高度 <= 1
    class Solution:def isBalanced(self, root: TreeNode) -> bool:def depth(root):if root is None:return 0, Truedl, bl = depth(root.left)dr, br = depth(root.right)return max(dl, dr) + 1, bl and br and abs(dl - dr) < 2_, out = depth(root)return out

    insert-into-a-binary-search-tree

    給定二叉搜索樹(shù)(BST)的根節(jié)點(diǎn)和要插入樹(shù)中的值,將值插入二叉搜索樹(shù)。 返回插入后二叉搜索樹(shù)的根節(jié)點(diǎn)。

    • 思路:如果只是為了完成任務(wù)則找到最后一個(gè)葉子節(jié)點(diǎn)滿足插入條件即可。
    class Solution:def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:if root is None:return TreeNode(val)node = rootwhile True:if val > node.val:if node.right is None:node.right = TreeNode(val)return rootelse:node = node.rightelse:if node.left is None:node.left = TreeNode(val)return rootelse:node = node.left

    練習(xí)

    • maximum-depth-of-binary-tree
    • balanced-binary-tree
    • binary-tree-maximum-path-sum
    • lowest-common-ancestor-of-a-binary-tree
    • binary-tree-level-order-traversal
    • binary-tree-level-order-traversal-ii
    • binary-tree-zigzag-level-order-traversal
    • validate-binary-search-tree
    • insert-into-a-binary-search-tree

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的LeetCode Hot100 ---- 二叉树专题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。