问题分析
本题要求在一个二叉树的节点上供应暖炉,使得整个树的所有节点都得到温暖。暖炉的温暖范围是父节点、自身和子节点。因此,我们希望通过最少的暖炉供应,覆盖整个二叉树。
问题的本质是一个“最小化覆盖”问题,具体来说是要在二叉树的节点上放置最少数量的暖炉,以确保所有节点都能被温暖到。
关键点:
-
树的节点关系:树是二叉树,每个节点有最多两个子节点。暖炉可以覆盖父节点、自身及其子节点。这个覆盖范围需要有效地被利用,来减少所需暖炉的数量。
-
树的遍历方式:我们可以使用深度优先搜索(DFS)来递归处理每个子树,并通过状态转移计算在每个节点处放暖炉的最小操作数。
-
状态建模:
- 未覆盖:当前节点和其子节点都未被暖炉覆盖。
- 已覆盖:当前节点已被覆盖,但是没有放置暖炉。
- 有暖炉:当前节点放置了暖炉。
解决方案
使用 动态规划 和 DFS (深度优先搜索)来遍历整个树。对于每个节点,考虑以下三种状态:
- 不覆盖:当前节点及其子树没有暖炉,需要子树的某些节点被覆盖。
- 已覆盖:当前节点不需要暖炉,但其子树已经有了暖炉。
- 有暖炉:当前节点放置了暖炉,能够覆盖自己和所有子节点。
DFS 状态转移
我们通过递归来定义树的三种状态:
no_cover:当前节点和子节点都没有被温暖覆盖,需要子节点提供暖炉。covered:当前节点已经被温暖覆盖,但没有放暖炉。with_heater:当前节点放置了暖炉,覆盖自身及其子节点。
递归过程:
- 对于叶子节点,直接判断是否需要暖炉。
- 对于非叶子节点,分别递归计算左子树和右子树的状态,并根据其子树的状态决定当前节点是否需要放暖炉。
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(nodes):
if not nodes or nodes[0] == 0:
return None
root = TreeNode(nodes[0])
queue = [root]
index = 1
while index < len(nodes):
current = queue.pop(0)
if current:
if index < len(nodes) and nodes[index] != 0:
current.left = TreeNode(nodes[index])
queue.append(current.left)
index += 1
if index < len(nodes) and nodes[index] != 0:
current.right = TreeNode(nodes[index])
queue.append(current.right)
index += 1
return root
def solution(nodes):
# DFS遍历树,返回三种状态的最小值
def dfs(node):
if not node:
return (0, 0, float('inf')) # (未覆盖,被覆盖,有暖炉)
left = dfs(node.left)
right = dfs(node.right)
# 当前节点未被覆盖
no_cover = left[1] + right[1]
# 当前节点被覆盖
covered = min(left[2] + right[1], left[1] + right[2], left[2] + right[2])
# 当前节点有暖炉
with_heater = 1 + min(left) + min(right)
return (no_cover, covered, with_heater)
root = build_tree(nodes)
result = dfs(root)
# 返回最小的操作数:可以选择有暖炉或者被覆盖
return min(result[1], result[2])
if __name__ == "__main__":
# 你可以添加更多的测试用例
print(solution([1, 1, 0, 1, 1]) == 1) # 输出:1
print(solution([1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1]) == 3) # 输出:3
print(solution([1, 1, 0, 1, 1, 1, 1]) == 2) # 输出:2
代码解释
-
TreeNode类:这是二叉树节点的定义,每个节点有val(节点的值)、left(左子树)和right(右子树)三个属性。 -
build_tree函数:根据给定的节点列表nodes构建二叉树。nodes是按层次遍历顺序给出的,0表示空节点,1表示存在节点。 -
dfs函数:递归函数,用来计算每个节点的三种状态。no_cover:当前节点和它的子树没有被覆盖,依赖子节点提供暖炉。covered:当前节点被覆盖,但没有放暖炉,意味着需要子树提供暖炉。with_heater:当前节点有暖炉,能够覆盖自己和子树。 递归过程中,dfs返回的状态是(no_cover, covered, with_heater),每个节点根据其子树的状态来决定最终需要的操作。
-
solution函数:调用dfs函数遍历树并返回最小的暖炉操作数,即在当前节点有暖炉和被覆盖两种状态中的较小值。