LeetCode Hot100 ---- 二叉树专题
樹(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)順序
前序非遞歸
- 本質(zhì)上是圖的DFS的一個(gè)特例,因此可以用棧來(lái)實(shí)現(xiàn)
中序非遞歸
中序遍歷的思想是:
1.若節(jié)點(diǎn)還有左子樹(shù),就要把左子樹(shù)訪問(wèn)完;
2.沒(méi)有左子樹(shù)可以訪問(wèn)時(shí),訪問(wèn)該節(jié)點(diǎn),并嘗試訪問(wèn)右子樹(shù)
后序非遞歸
后序遍歷(迭代法)
再來(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>
分治法應(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:層序遍歷
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ù)。
思路:
分治法: 一個(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 True543. 二叉樹(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 - 1102. 二叉樹(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 resinsert-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 root437. 路徑總和 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 t3226. 翻轉(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 root114. 二叉樹(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_right96. 不同的二叉搜索樹(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 root236. 二叉樹(shù)的最近公共祖先
給定一個(gè)二叉樹(shù), 找到該樹(shù)中兩個(gè)指定節(jié)點(diǎn)的最近公共祖先。
所有節(jié)點(diǎn)的值都是唯一的。p、q 為不同節(jié)點(diǎn)且均存在于給定的二叉樹(shù)中。
思路:
某節(jié)點(diǎn)是公共祖先有兩種情況:
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 root337. 打家劫舍 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 True297. 二叉樹(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),最大值有四種情況:
balanced-binary-tree
給定一個(gè)二叉樹(shù),判斷它是否是高度平衡的二叉樹(shù)。
- 思路 1:分治法,左邊平衡 && 右邊平衡 && 左右兩邊高度 <= 1
insert-into-a-binary-search-tree
給定二叉搜索樹(shù)(BST)的根節(jié)點(diǎn)和要插入樹(shù)中的值,將值插入二叉搜索樹(shù)。 返回插入后二叉搜索樹(shù)的根節(jié)點(diǎn)。
- 思路:如果只是為了完成任務(wù)則找到最后一個(gè)葉子節(jié)點(diǎn)滿足插入條件即可。
練習(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
總結(jié)
以上是生活随笔為你收集整理的LeetCode Hot100 ---- 二叉树专题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 两轮车概念股票龙头一览表,2022两轮车
- 下一篇: LeetCode Hot100 ----