二叉树供暖问题 | 豆包MarsCode AI刷题

48 阅读4分钟

问题分析

本题要求在一个二叉树的节点上供应暖炉,使得整个树的所有节点都得到温暖。暖炉的温暖范围是父节点、自身和子节点。因此,我们希望通过最少的暖炉供应,覆盖整个二叉树。

问题的本质是一个“最小化覆盖”问题,具体来说是要在二叉树的节点上放置最少数量的暖炉,以确保所有节点都能被温暖到。

关键点:

  1. 树的节点关系:树是二叉树,每个节点有最多两个子节点。暖炉可以覆盖父节点、自身及其子节点。这个覆盖范围需要有效地被利用,来减少所需暖炉的数量。

  2. 树的遍历方式:我们可以使用深度优先搜索(DFS)来递归处理每个子树,并通过状态转移计算在每个节点处放暖炉的最小操作数。

  3. 状态建模

    • 未覆盖:当前节点和其子节点都未被暖炉覆盖。
    • 已覆盖:当前节点已被覆盖,但是没有放置暖炉。
    • 有暖炉:当前节点放置了暖炉。

解决方案

使用 动态规划DFS (深度优先搜索)来遍历整个树。对于每个节点,考虑以下三种状态:

  1. 不覆盖:当前节点及其子树没有暖炉,需要子树的某些节点被覆盖。
  2. 已覆盖:当前节点不需要暖炉,但其子树已经有了暖炉。
  3. 有暖炉:当前节点放置了暖炉,能够覆盖自己和所有子节点。

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

代码解释

  1. TreeNode:这是二叉树节点的定义,每个节点有 val(节点的值)、left(左子树)和 right(右子树)三个属性。

  2. build_tree 函数:根据给定的节点列表 nodes 构建二叉树。nodes 是按层次遍历顺序给出的,0 表示空节点,1 表示存在节点。

  3. dfs 函数:递归函数,用来计算每个节点的三种状态。

    • no_cover:当前节点和它的子树没有被覆盖,依赖子节点提供暖炉。
    • covered:当前节点被覆盖,但没有放暖炉,意味着需要子树提供暖炉。
    • with_heater:当前节点有暖炉,能够覆盖自己和子树。 递归过程中,dfs 返回的状态是 (no_cover, covered, with_heater),每个节点根据其子树的状态来决定最终需要的操作。
  4. solution 函数:调用 dfs 函数遍历树并返回最小的暖炉操作数,即在当前节点有暖炉和被覆盖两种状态中的较小值。