以题代练
二叉树中的最大路径和
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
示例 1:
输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
示例 2:
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
这个问题要求找出二叉树中的一条路径,使得该路径上的节点值之和最大。关键在于理解,这个路径不一定要经过根节点,它可以是树中任意两个节点间的路径。
为了解决这个问题,可以使用递归。对于每个节点,考虑四种情况:
- 该节点单独构成一条路径。
- 最大路径通过该节点的左子节点。
- 最大路径通过该节点的右子节点。
- 最大路径通过该节点的左子节点,然后是该节点,然后是右子节点。
递归地计算每个节点作为路径起点的最大值,并更新全局最大路径和。需要注意的是,当计算节点的最大贡献值时,只能选择一条路径(左子节点或右子节点),因为路径不能分叉。
下面是解决这个问题的Python代码示例:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def maxPathSum(self, root: TreeNode) -> int:
def max_gain(node):
nonlocal max_sum
if not node:
return 0
# 计算左子节点和右子节点的最大贡献值,负贡献则忽略
left_gain = max(max_gain(node.left), 0)
right_gain = max(max_gain(node.right), 0)
# 节点的最大路径和取决于该节点的值加上左右子节点的最大贡献值
price_newpath = node.val + left_gain + right_gain
# 更新全局最大路径和
max_sum = max(max_sum, price_newpath)
# 返回节点的最大贡献值
return node.val + max(left_gain, right_gain)
max_sum = float('-inf')
max_gain(root)
return max_sum
这段代码首先定义了一个二叉树节点的类TreeNode
。然后,定义了一个解决方案类Solution
,其中包含了一个方法maxPathSum
。这个方法接受一个TreeNode
作为根节点,并返回二叉树的最大路径和。方法中的max_gain
函数用于计算每个节点的最大贡献值,并更新全局最大路径和max_sum
。最后,返回max_sum
作为结果。
最长同值路径
给定一个二叉树的 root ,返回 最长的路径的长度 ,这个路径中的 每个节点具有相同值 。 这条路径可以经过也可以不经过根节点。
两个节点之间的路径长度 由它们之间的边数表示。
示例 1:
输入:root = [5,4,5,1,1,5]
输出:2
示例 2:
输入:root = [1,4,5,4,4,5]
输出:2
同值路径指的是路径上所有节点的值都相同。这个问题可以通过深度优先搜索(DFS)来解决。
需要定义一个递归函数,用于遍历树的每个节点。对于每个节点,检查其左子节点和右子节点的值是否与当前节点相同。如果相同,将该子节点的路径长度(递归计算得到)加1,否则不考虑该子节点(因为它不是同值路径的一部分)。
接着,更新全局最大同值路径长度。对于每个节点,最长同值路径可能是经过该节点的左子节点和右子节点的路径,也可能是只经过左子节点或右子节点的路径。
下面是解决这个问题的 Python 代码示例:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def longestUnivaluePath(self, root: TreeNode) -> int:
self.ans = 0
def dfs(node):
if not node:
return 0
# 遍历左子节点和右子节点
left_length = dfs(node.left)
right_length = dfs(node.right)
# 检查左子节点和右子节点与当前节点的值是否相同
left_path = right_path = 0
if node.left and node.left.val == node.val:
left_path = left_length + 1
if node.right and node.right.val == node.val:
right_path = right_length + 1
# 更新全局最长同值路径
self.ans = max(self.ans, left_path + right_path)
# 返回以当前节点为终点的最长同值路径长度
return max(left_path, right_path)
dfs(root)
return self.ans
这段代码中,首先定义了二叉树节点的类TreeNode
。然后,定义了一个解决方案类Solution
,其中包含了一个方法longestUnivaluePath
。这个方法接受一个TreeNode
作为根节点,并返回二叉树中最长同值路径的长度。dfs
函数用于递归地计算每个节点的最长同值路径长度,并在过程中更新全局最大长度self.ans
。最后,返回self.ans
作为结果。
二叉树的坡度
给你一个二叉树的根节点 root ,计算并返回 整个树 的坡度 。
一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。
整个树 的坡度就是其所有节点的坡度之和。
示例 1:
输入:root = [1,2,3]
输出:1
解释:
节点 2 的坡度:|0-0| = 0(没有子节点)
节点 3 的坡度:|0-0| = 0(没有子节点)
节点 1 的坡度:|2-3| = 1(左子树就是左子节点,所以和是 2 ;右子树就是右子节点,所以和是 3 )
坡度总和:0 + 0 + 1 = 1
示例 2:
输入:root = [4,2,9,3,5,null,7]
输出:15
解释:
节点 3 的坡度:|0-0| = 0(没有子节点)
节点 5 的坡度:|0-0| = 0(没有子节点)
节点 7 的坡度:|0-0| = 0(没有子节点)
节点 2 的坡度:|3-5| = 2(左子树就是左子节点,所以和是 3 ;右子树就是右子节点,所以和是 5 )
节点 9 的坡度:|0-7| = 7(没有左子树,所以和是 0 ;右子树正好是右子节点,所以和是 7 )
节点 4 的坡度:|(3+5+2)-(9+7)| = |10-16| = 6(左子树值为 3、5 和 2 ,和是 10 ;右子树值为 9 和 7 ,和是 16 )
坡度总和:0 + 0 + 0 + 2 + 7 + 6 = 15
示例 3:
输入:root = [21,7,14,1,1,2,2,3,3]
输出:9
为了解决这个问题,需要计算二叉树中每个节点的坡度,并将它们相加得到整个树的坡度。节点的坡度定义为该节点的左子树节点值之和与右子树节点值之和的差的绝对值。如果某个子树不存在,则其值为0。
这个问题可以通过递归方法解决。对于每个节点,需要做以下几件事:
- 计算左子树的所有节点值之和。
- 计算右子树的所有节点值之和。
- 计算当前节点的坡度,并加到总坡度上。
- 返回当前节点及其子树的所有节点值之和,以便上层节点计算坡度。
下面是解决这个问题的Python代码示例:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def findTilt(self, root: TreeNode) -> int:
self.total_tilt = 0
def sum_and_tilt(node):
if not node:
return 0
left_sum = sum_and_tilt(node.left)
right_sum = sum_and_tilt(node.right)
# 当前节点的坡度
tilt = abs(left_sum - right_sum)
self.total_tilt += tilt
# 返回当前节点及其子树的值之和
return node.val + left_sum + right_sum
sum_and_tilt(root)
return self.total_tilt
这段代码首先定义了一个二叉树节点的类TreeNode
。然后,定义了一个解决方案类Solution
,其中包含了一个方法findTilt
。这个方法接受一个TreeNode
作为根节点,并返回整个树的坡度。在方法中,sum_and_tilt
函数用于递归地计算每个节点的子树和及其坡度,并累加到self.total_tilt
。最后,返回self.total_tilt
作为整个树的坡度。
路径总和
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:[]
示例 3:
输入:root = [1,2], targetSum = 0
输出:[]
为了解决这个问题,需要找到二叉树中所有从根节点到叶子节点的路径,其路径上的节点值之和等于给定的目标和 targetSum
。
这可以通过递归的方式来实现。对于每个节点,需要做以下几件事:
- 检查当前节点是否为叶子节点,且其值等于剩余的目标和(
targetSum
- 当前节点的值)。如果是,那么找到了一条符合条件的路径。 - 如果当前节点不是叶子节点,递归地在其左子树和右子树中继续寻找符合条件的路径。
- 从根节点到当前节点的路径应该被记录下来,以便能够重建整条路径。
下面是解决这个问题的Python代码示例:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
paths = []
self.find_paths(root, targetSum, [], paths)
return paths
def find_paths(self, node, target, path, paths):
if node is None:
return
# 将当前节点加入到路径中
path.append(node.val)
# 检查是否到达叶子节点且路径和等于目标和
if node.left is None and node.right is None and target == node.val:
paths.append(list(path))
else:
# 继续在左右子树中寻找
self.find_paths(node.left, target - node.val, path, paths)
self.find_paths(node.right, target - node.val, path, paths)
# 回溯前,移除路径中的当前节点
path.pop()
这段代码首先定义了一个二叉树节点的类TreeNode
。然后,定义了一个解决方案类Solution
,其中包含了一个方法pathSum
。这个方法接受一个TreeNode
作为根节点和一个整数targetSum
,返回所有符合条件的路径。在方法中,find_paths
函数用于递归地在树中查找路径,并将符合条件的路径添加到paths
列表中。
二叉树的所有路径
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]
示例 2:
输入:root = [1]
输出:[“1”]
为了解决这个问题,需要找出二叉树中所有从根节点到叶子节点的路径。这个任务可以通过递归方法实现。
对于每个节点,需要执行以下步骤:
- 检查当前节点是否为叶子节点(没有子节点)。如果是,就将当前路径添加到结果列表中。
- 如果当前节点不是叶子节点,则递归地在其左子树和右子树中继续搜索路径。
- 在搜索路径时,需要将当前节点的值加入到路径字符串中。
下面是解决这个问题的Python代码示例:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def binaryTreePaths(self, root: TreeNode) -> List[str]:
if not root:
return []
paths = []
self.find_paths(root, "", paths)
return paths
def find_paths(self, node, path, paths):
if node:
# 构建到当前节点的路径
path += str(node.val)
# 检查当前节点是否为叶子节点
if not node.left and not node.right:
paths.append(path)
else:
# 继续在左右子树中寻找路径
path += "->"
self.find_paths(node.left, path, paths)
self.find_paths(node.right, path, paths)
这段代码首先定义了一个二叉树节点的类TreeNode
。然后,定义了一个解决方案类Solution
,其中包含了一个方法binaryTreePaths
。这个方法接受一个TreeNode
作为根节点,并返回所有从根节点到叶子节点的路径。在方法中,find_paths
函数用于递归地在树中查找路径,并将找到的每条路径添加到paths
列表中。
单值二叉树
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true;否则返回 false。
示例 1:
输入:[1,1,1,1,1,null,1]
输出:true
示例 2:
输入:[2,2,2,5,2]
输出:false
为了解决这个问题,需要检查二叉树中的每个节点是否都具有相同的值。这可以通过递归方法实现。
对于每个节点,需要执行以下步骤:
- 检查当前节点是否为空。如果是空节点,则返回
True
(空节点不违反单值二叉树的定义)。 - 检查当前节点的值是否与其父节点的值相同(如果存在父节点)。
- 递归地检查当前节点的左子节点和右子节点是否都具有相同的值。
如果所有节点都通过这些检查,那么该二叉树就是单值二叉树。
下面是解决这个问题的Python代码示例:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def isUnivalTree(self, root: TreeNode) -> bool:
def check(node, val):
if not node:
return True
if node.val != val:
return False
return check(node.left, val) and check(node.right, val)
return check(root, root.val) if root else True
这段代码首先定义了一个二叉树节点的类TreeNode
。然后,定义了一个解决方案类Solution
,其中包含了一个方法isUnivalTree
。这个方法接受一个TreeNode
作为根节点,并返回一个布尔值,表示这棵树是否是单值二叉树。在方法中,check
函数用于递归地检查每个节点是否具有与给定值相同的值。如果树为空,默认其为单值二叉树,返回True
。