二叉树供暖问题

73 阅读3分钟

from bisect import bisect_right from typing import Literal

def solution(nodes: list[int]) -> int: ans = 0 if not nodes or nodes[0] == 0: return ans indices: list[int] = [1] # 所有存在的节点的索引 parent, parents = 1, [] i, size = 1, len(nodes) while i < size: # 将左右子节点加入队列parents if nodes[i]: parents.append(index := 2 * parent) indices.append(index) i += 1 if i == size: break if nodes[i]: parents.append(index := 2 * parent + 1) indices.append(index) i += 1 if parents: parent = parents.pop(0) else: break def inspect(index: int) -> Literal[0, 1, 2]: """ Returns ------- 0: not covered; 1: covered; 2: installed. """ nonlocal ans if indices[bisect_right(indices, index) - 1] != index: return 1 left, right = inspect(double := 2 * index), inspect(double + 1) if left == right == 1: return 0 if 0 in (left, right): ans += 1 return 2 if 2 in (left, right): return 1 return 0 if inspect(1) == 0: ans += 1 return ans 问题概述 给定一个整数列表 nodes,该列表表示一个完全二叉树的节点状态,其中 nodes[i] 表示节点 i 的状态(0 表示不存在,1 表示存在但未安装设备,2 表示已安装设备)。目标是计算为了使所有存在的节点都被设备覆盖(即每个存在的节点的子节点或自身至少有一个设备),需要额外安装的最少设备数量。

解题思路 初始化: 创建一个列表 indices 来存储所有存在节点的索引。 使用一个队列 parents 来存储需要检查的父节点。 使用变量 parent 来跟踪当前父节点的索引。 使用变量 i 来遍历 nodes 列表,size 存储 nodes 的长度。 构建存在节点的索引列表: 遍历 nodes 列表,对于每个存在的节点(值为 1 或 2),将其索引添加到 indices 和 parents 队列中。 更新 parent 变量以指向下一个要检查的父节点。 定义递归函数 inspect: inspect 函数用于递归检查每个节点,并返回三种状态之一:0(未覆盖),1(已覆盖但无设备),2(已安装设备)。 使用 bisect_right 从 indices 中找到当前节点的索引位置,以快速确定节点是否存在。 递归检查左右子节点,并根据子节点的状态更新当前节点的状态。 如果当前节点的左右子节点都未安装设备但存在(即状态为 1),则需要在当前节点安装设备(将 ans 加一)。 如果当前节点的左右子节点中有一个已安装设备(状态为 2),则当前节点被视为已覆盖(返回 1)。 如果当前节点安装了设备(状态为 2),则直接返回 2。 调用 inspect 函数并返回结果: 从根节点(索引 1)开始调用 inspect 函数。 如果根节点未被覆盖(返回 0),则需要在根节点安装一个设备(将 ans 加一)。 返回最终的设备数量 ans。 算法总结 时间复杂度:由于每个节点最多被访问一次,且使用了二分查找来快速确定节点是否存在,因此时间复杂度为 O(n log n),其中 n 是节点的数量。 空间复杂度:使用了额外的空间来存储存在的节点索引(indices)和待检查的父节点队列(parents),因此空间复杂度为 O(n)。 优点 使用递归函数 inspect 清晰地表示了节点之间的依赖关系。 通过 bisect_right 实现了快速查找节点索引,提高了效率。 代码结构清晰,易于理解和维护。