513.找树左下角的值
给定一个二叉树,在树的最后一行找到最左边的值。
迭代法:
from collections import deque
class Solution:
def findBottomLeftValue(self, root):
if root is None:
return 0
queue = deque()
queue.append(root)
result = 0
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
if i == 0:
result = node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
解释:
from collections import deque
这行代码导入了Python标准库中的deque
模块,它提供了双向队列数据结构的实现。
class Solution:
def findBottomLeftValue(self, root):
这部分定义了一个名为Solution
的类,其中包含了一个名为findBottomLeftValue
的方法。这个方法接受一个root
参数,表示二叉树的根节点。
if root is None:
return 0
这个条件判断语句检查根节点是否为None
,如果是,则直接返回0。
queue = deque()
queue.append(root)
result = 0
这部分代码创建了一个空的双向队列queue
,并将根节点添加到队列中。同时,还定义了一个变量result
并将其初始化为0,用于存储最终的结果值(即二叉树最底层最左边的节点的值)。
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
if i == 0:
result = node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
这个循环迭代地处理队列中的节点。首先,获取当前队列的长度size
,以便在内部循环中逐个处理每个节点。然后,通过queue.popleft()
从队列的左侧弹出一个节点,并将其存储在变量node
中。
在循环中,如果i
等于0(即当前节点是该层的第一个节点),则将node.val
赋值给result
,以便在循环结束后返回最底层最左边节点的值。
接下来,如果节点的左子节点存在,则将其添加到队列中。同样地,如果节点的右子节点存在,则也将其添加到队列中。
循环将一直进行,直到队列中没有节点,即已经处理完二叉树的所有节点。
return result
最后,该方法返回存储在result
中的结果值。也就是最底层最左边节点的值。
这段代码的目的是找到二叉树中最底层最左边的节点的值,并将其作为结果返回。它使用了队列这个数据结构来实现广度优先搜索(BFS)的方式来遍历二叉树。
迭代法:
class Solution:
def findBottomLeftValue(self, root: TreeNode) -> int:
self.max_depth = float('-inf')
self.result = None
self.traversal(root, 0)
return self.result
def traversal(self, node, depth):
if not node.left and not node.right:
if depth > self.max_depth:
self.max_depth = depth
self.result = node.val
return
if node.left:
depth += 1
self.traversal(node.left, depth)
depth -= 1
if node.right:
depth += 1
self.traversal(node.right, depth)
depth -= 1
这段代码是一个定义了一个名为`Solution`的类,其中包含了`findBottomLeftValue`和`traversal`两个方法。
`findBottomLeftValue`方法接受一个名为`root`的参数,表示树的根节点。该方法的作用是找到树的最底层最左边的节点的值。它首先初始化了`max_depth`为负无穷大,并且将`result`初始化为`None`。然后调用`traversal`方法来遍历树,并记录下最底层最左边的节点的值。最后返回`result`。
`traversal`方法接受两个参数,一个是`node`表示当前节点,另一个是`depth`表示当前节点的深度。它首先判断当前节点是否为叶子节点(即没有左右子节点),如果是,则比较当前节点的深度和`max_depth`,如果当前节点的深度大于`max_depth`,则更新`max_depth`为当前节点的深度,并将`result`设置为当前节点的值。然后返回。
如果当前节点有左子节点,那么先将`depth`加一,然后递归调用`traversal`方法来遍历左子节点,并在方法返回后将`depth`减一,恢复原来的深度。同理,如果当前节点有右子节点,也是如此处理。
这样,通过递归的方式遍历整棵树,找到最底层最左边的节点的值,并将结果保存在`result`中返回。
112. 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
class Solution:
def traversal(self, cur: TreeNode, count: int) -> bool:
if not cur.left and not cur.right and count == 0: # 遇到叶子节点,并且计数为0
return True
if not cur.left and not cur.right: # 遇到叶子节点直接返回
return False
if cur.left: # 左
count -= cur.left.val
if self.traversal(cur.left, count): # 递归,处理节点
return True
count += cur.left.val # 回溯,撤销处理结果
if cur.right: # 右
count -= cur.right.val
if self.traversal(cur.right, count): # 递归,处理节点
return True
count += cur.right.val # 回溯,撤销处理结果
return False
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if root is None:
return False
return self.traversal(root, sum - root.val)
-
class Solution:
这一行定义了一个名为Solution
的类,这是你的解决方案的主体。 -
def traversal(self, cur: TreeNode, count: int) -> bool:
这是一个名为traversal
的方法,它接收两个参数:cur
(当前节点)和count
(当前路径的总和)。这个方法返回一个布尔值,表示是否存在一条从当前节点到叶子节点的路径,使得路径上的节点值之和等于count
。 -
if not cur.left and not cur.right and count == 0:
这一行检查当前节点是否是叶子节点(即没有左右子节点),并且count
是否为0。如果是,那么就找到了一条满足条件的路径,返回True
。 -
if not cur.left and not cur.right:
这一行再次检查当前节点是否是叶子节点。如果是,但count
不为0,那么就没有找到满足条件的路径,返回False
。 -
接下来的几行代码分别处理当前节点的左子节点和右子节点。如果存在左子节点或右子节点,就从
count
中减去该子节点的值,然后递归调用traversal
方法。如果递归调用返回True
,那么就找到了一条满足条件的路径,返回True
。否则,就撤销之前的操作(即将子节点的值加回到count
中),然后处理另一个子节点。 -
如果遍历完所有的子节点都没有找到满足条件的路径,那么返回
False
。 -
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
这是一个名为hasPathSum
的方法,它接收两个参数:root
(根节点)和sum
(目标总和)。这个方法返回一个布尔值,表示是否存在一条从根节点到叶子节点的路径,使得路径上的节点值之和等于sum
。 -
首先,检查根节点是否为空。如果为空,则直接返回
False
。 -
然后,调用
traversal
方法来查找是否存在满足条件的路径。注意这里传入的count
值是sum - root.val
,因为我们已经包含了根节点的值。
113. 路径总和ii
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
class Solution:
def __init__(self):
self.result = []
self.path = []
def traversal(self, cur, count):
if not cur.left and not cur.right and count == 0: # 遇到了叶子节点且找到了和为sum的路径
self.result.append(self.path[:])
return
if not cur.left and not cur.right: # 遇到叶子节点而没有找到合适的边,直接返回
return
if cur.left: # 左 (空节点不遍历)
self.path.append(cur.left.val)
count -= cur.left.val
self.traversal(cur.left, count) # 递归
count += cur.left.val # 回溯
self.path.pop() # 回溯
if cur.right: # 右 (空节点不遍历)
self.path.append(cur.right.val)
count -= cur.right.val
self.traversal(cur.right, count) # 递归
count += cur.right.val # 回溯
self.path.pop() # 回溯
return
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
self.result.clear()
self.path.clear()
if not root:
return self.result
self.path.append(root.val) # 把根节点放进路径
self.traversal(root, sum - root.val)
return self.result
这段Python代码的目的是找到二叉树中所有从根到叶子的路径,它们的和等于给定的目标值。代码使用了一个叫做Solution的类,它有三个属性:result, path, 和 traversal。result属性是一个列表,用来存储符合条件的最终路径。path属性是一个列表,用来存储从根节点到某个节点的当前路径。traversal属性是一个方法,它接受两个参数:cur和count。cur是当前节点,count是剩余要匹配的和。 traversal方法使用递归和回溯来遍历树中所有可能的路径。它有四个基本情况:
• 如果当前节点是叶子节点(没有左右子节点)并且count为零,说明路径达到了目标和,所以把path列表的一个副本添加到result列表中,并返回。
• 如果当前节点是叶子节点并且count不为零,说明路径没有匹配目标和,所以什么都不做,直接返回。
• 如果当前节点没有左子节点,跳过左子树,只遍历右子树。
• 如果当前节点没有右子节点,跳过右子树,只遍历左子树。 对于每次递归调用,方法执行以下步骤:
• 把当前节点的子节点(左或右)的值添加到path列表中,并从count中减去它。
• 用当前节点的子节点和更新后的count作为参数调用自己。
• 把当前节点的子节点的值加回到count中,并从path列表中弹出。这是为了撤销之前的改变,并恢复原始状态以便探索其他路径。 pathSum方法是主函数,它接受两个参数:root和sum。root是二叉树的根节点,sum是要匹配的目标值。方法初始化result和path列表为空列表,并检查root是否为None。如果是,就返回一个空的result列表。否则,就把根节点的值添加到path列表中,并用根节点和sum减去根节点的值作为参数调用traversal方法。最后,返回result列表。
106.从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出
- 中序遍历 inorder = [9,3,15,20,7]
- 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
# 第一步: 特殊情况讨论: 树为空. (递归终止条件)
if not postorder:
return None
# 第二步: 后序遍历的最后一个就是当前的中间节点.
root_val = postorder[-1]
root = TreeNode(root_val)
# 第三步: 找切割点.
separator_idx = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
# 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
# ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
postorder_left = postorder[:len(inorder_left)]
postorder_right = postorder[len(inorder_left): len(postorder) - 1]
# 第六步: 递归
root.left = self.buildTree(inorder_left, postorder_left)
root.right = self.buildTree(inorder_right, postorder_right)
# 第七步: 返回答案
return root
这段Python代码的目的是根据中序遍历和后序遍历的结果重建二叉树。代码使用了一个叫做Solution的类,它有一个方法:buildTree。buildTree方法接受两个参数:inorder和postorder,分别是二叉树的中序遍历和后序遍历的列表。方法返回一个TreeNode对象,表示重建后的二叉树的根节点。
buildTree方法使用递归的思想来实现,它有七个步骤:
• 第一步:特殊情况讨论:树为空。(递归终止条件)如果postorder列表为空,说明没有节点,直接返回None。
• 第二步:后序遍历的最后一个元素就是当前的根节点。从postorder列表中取出最后一个元素作为根节点的值,并创建一个TreeNode对象。
• 第三步:找切割点。在inorder列表中找到根节点的值所在的位置,这个位置就是切割点,它把inorder列表分成了左子树和右子树的部分。
• 第四步:切割inorder列表。得到inorder列表的左半边和右半边,分别表示左子树和右子树的中序遍历结果。
• 第五步:切割postorder列表。得到postorder列表的左半边和右半边,分别表示左子树和右子树的后序遍历结果。注意,这里要保证切割后的列表大小跟inorder列表切割后的大小一致,所以要去掉postorder列表最后一个元素(即根节点)。
• 第六步:递归。用inorder列表和postorder列表的左半边作为参数调用buildTree方法,得到左子树的根节点,并赋值给当前根节点的左孩子。用inorder列表和postorder列表的右半边作为参数调用buildTree方法,得到右子树的根节点,并赋值给当前根节点的右孩子。
• 第七步:返回答案。返回当前根节点。
105.从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树:
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
# 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
if not preorder:
return None
# 第二步: 前序遍历的第一个就是当前的中间节点.
root_val = preorder[0]
root = TreeNode(root_val)
# 第三步: 找切割点.
separator_idx = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
# 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
# ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的.
preorder_left = preorder[1:1 + len(inorder_left)]
preorder_right = preorder[1 + len(inorder_left):]
# 第六步: 递归
root.left = self.buildTree(preorder_left, inorder_left)
root.right = self.buildTree(preorder_right, inorder_right)
# 第七步: 返回答案
return root
这段Python代码的目的是根据前序遍历和中序遍历的结果重建二叉树。代码使用了一个叫做Solution的类,它有一个方法:buildTree。buildTree方法接受两个参数:preorder和inorder,分别是二叉树的前序遍历和中序遍历的列表。方法返回一个TreeNode对象,表示重建后的二叉树的根节点。
buildTree方法使用递归的思想来实现,它有七个步骤:
• 第一步:特殊情况讨论:树为空。(递归终止条件)如果preorder列表为空,说明没有节点,直接返回None。
• 第二步:前序遍历的第一个元素就是当前的根节点。从preorder列表中取出第一个元素作为根节点的值,并创建一个TreeNode对象。
• 第三步:找切割点。在inorder列表中找到根节点的值所在的位置,这个位置就是切割点,它把inorder列表分成了左子树和右子树的部分。
• 第四步:切割inorder列表。得到inorder列表的左半边和右半边,分别表示左子树和右子树的中序遍历结果。
• 第五步:切割preorder列表。得到preorder列表的左半边和右半边,分别表示左子树和右子树的前序遍历结果。注意,这里要保证切割后的列表大小跟inorder列表切割后的大小一致,所以要去掉preorder列表第一个元素(即根节点)。
• 第六步:递归。用preorder列表和inorder列表的左半边作为参数调用buildTree方法,得到左子树的根节点,并赋值给当前根节点的左孩子。用preorder列表和inorder列表的右半边作为参数调用buildTree方法,得到右子树的根节点,并赋值给当前根节点的右孩子。
• 第七步:返回答案。返回当前根节点。