解题报告: 二叉树供暖 | 豆包MarsCode AI刷题

51 阅读5分钟

题目描述

天气越来越冷了,村子里有留守老人缺少照顾,会在这个冬天里挨冻,小华想运用自己的知识帮帮他们。已知村庄里每户人家作为一个节点,这些节点可以组成一个二叉树。我们可以在二叉树的节点上供应暖炉,每个暖炉可以为该节点的父节点、自身及其子节点带来温暖。给定一棵二叉树,求使个村庄都暖和起来至少需要供应多少个暖炉?

输入格式

输入为一个数组,按层遍历顺序描述二叉树的节点情况。值为 1,代表存在一个节点,值为 0,代表不存在该节点。

输出格式

输出最少暖炉供应数量。

输入样例

1, 1, 0, 1, 1

输出样例

1

数据范围

树的节点数的范围是 [1, 1000]。


解题思路

问题转化

本问题可以视为“二叉树摄像头覆盖问题”的变种,目标是通过在二叉树的某些节点放置暖炉,使得所有节点都被覆盖的同时,暖炉的数量最少。

贪心算法设计

本问题适合采用 贪心算法,通过后序遍历(先处理子节点,再处理父节点)的方式决定暖炉的放置位置。

贪心策略
  1. 如果某节点的子节点需要被覆盖,应在当前节点放置暖炉。
  2. 如果某节点本身需要被覆盖且其子节点已被覆盖,应在其父节点放置暖炉。
  3. 优先考虑远离根节点的节点覆盖需求,逐层往根节点推进。
节点状态定义

通过递归的后序遍历,为每个节点定义以下三种状态:

  • 状态 0:该节点需要供暖。
  • 状态 1:该节点放置了暖炉。
  • 状态 2:该节点已被覆盖(由其子节点或父节点的暖炉覆盖)。

根节点处理完成后,如果它仍需要供暖,则在根节点放置一个暖炉。

算法步骤

  1. 构建二叉树:根据层序遍历数组构建二叉树。
  2. 后序遍历:从叶子节点开始递归处理,根据子节点的状态决定当前节点是否需要放置暖炉。
  3. 根节点检查:最后检查根节点是否需要放置暖炉。
  4. 输出结果:返回总共放置的暖炉数量。

代码实现

二叉树节点类

class TreeNode:
    def __init__(self):
        self.left = None
        self.right = None

主算法实现

class Solution:
    def __init__(self):
        self.heaters = 0

    def minHeaters(self, root: TreeNode) -> int:
        def dfs(node: TreeNode) -> int:
            if not node:
                return 2  # 空节点视为已覆盖

            left = dfs(node.left)
            right = dfs(node.right)

            # 子节点需要供暖
            if left == 0 or right == 0:
                self.heaters += 1
                return 1  # 当前节点放置暖炉

            # 子节点已经覆盖
            if left == 1 or right == 1:
                return 2  # 当前节点已被覆盖

            # 子节点未覆盖,当前节点需要供暖
            return 0

        # 检查根节点
        if dfs(root) == 0:
            self.heaters += 1

        return self.heaters

构建二叉树的辅助函数

from collections import deque

def build_tree(nodes: list) -> TreeNode:
    if not nodes or nodes[0] == 0:
        return None

    root = TreeNode()
    queue = deque([root])
    index = 1
    n = len(nodes)

    while queue and index < n:
        current = queue.popleft()
        
        # 左子节点
        if index < n and nodes[index] == 1:
            current.left = TreeNode()
            queue.append(current.left)
        index += 1

        # 右子节点
        if index < n and nodes[index] == 1:
            current.right = TreeNode()
            queue.append(current.right)
        index += 1

    return root

完整代码

from typing import Optional
from collections import deque

# 定义二叉树节点
class TreeNode:
    def __init__(self):
        self.left: Optional['TreeNode'] = None
        self.right: Optional['TreeNode'] = None

# 主解决方案类
class Solution:
    def __init__(self):
        self.heaters = 0

    def minHeaters(self, root: Optional[TreeNode]) -> int:
        # 定义后序遍历函数
        def dfs(node: Optional[TreeNode]) -> int:
            if not node:
                return 2  # 空节点视为已覆盖

            left = dfs(node.left)
            right = dfs(node.right)

            if left == 0 or right == 0:
                self.heaters += 1
                return 1  # 当前节点放置暖炉

            if left == 1 or right == 1:
                return 2  # 当前节点被覆盖

            return 0  # 当前节点需要被覆盖

        if dfs(root) == 0:
            self.heaters += 1

        return self.heaters

# 构建二叉树的函数
def build_tree(nodes: list) -> Optional[TreeNode]:
    if not nodes or nodes[0] == 0:
        return None

    root = TreeNode()
    queue = deque([root])
    index = 1
    n = len(nodes)

    while queue and index < n:
        current = queue.popleft()
        
        # 处理左孩子
        if index < n and nodes[index] == 1:
            current.left = TreeNode()
            queue.append(current.left)
        index += 1

        # 处理右孩子
        if index < n and nodes[index] == 1:
            current.right = TreeNode()
            queue.append(current.right)
        index += 1

    return root

def solution(nodes):
    tree = build_tree(nodes)
    return Solution().minHeaters(tree)

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution([1, 1, 0, 1, 1]) == 1 )
    print(solution([1,0,1,1,0,1,0,1,0,1,0,0,1,1]) == 3 )
    print(solution([1,1,0,0,1,1,0,0,1,0,1,1,0,0,1]) == 3 )


示例运行

输入

1, 1, 0, 1, 1

运行过程

构建的二叉树结构:

    1
   /
  1
 / \
1   1
后序遍历:
  1. 从叶子节点 1 开始,状态为 0,需要供暖。
  2. 父节点 1 放置暖炉,状态变为 1
  3. 根节点被覆盖,状态为 2

最终结果:需要 1 个暖炉。

输出

1

时间与空间复杂度分析

时间复杂度

  • 二叉树构建:O(N),其中 N 是节点数量。
  • 后序遍历:O(N),每个节点只被访问一次。

总复杂度:O(N)。

空间复杂度

  • 递归栈深度:O(H),其中 H 是树的高度,最坏情况下为 O(N)(退化为链表)。
  • 队列空间:O(N)。

总复杂度:O(N)。


总结与反思

  • 该算法思路可以推广到类似的“覆盖问题”:
    • 如二叉树摄像头问题。
    • 图论中基于顶点的覆盖问题。
  • 在更大规模或动态变化的树中,可考虑动态规划优化递归。