news 2026/9/12 13:10:11

968. Binary Tree Cameras 二叉树监控(贪心 + 树形 DP 状态机)题解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
968. Binary Tree Cameras 二叉树监控(贪心 + 树形 DP 状态机)题解

968. Binary Tree Cameras 二叉树监控(贪心 + 树形 DP 状态机)题解

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

导读

本篇基于 LeetCode-Go 仓库中 968. Binary Tree Cameras 题解文档,完整讲解「二叉树监控」这道经典贪心/树形 DP 题:如何用最少的摄像头覆盖整棵二叉树的所有节点。文章先给出题目与核心思想,再结合仓库内 完整 Go 实现 与 单元测试 逐行剖析贪心染色法与三状态递归实现,最后给出测试运行与复杂度分析,帮助你彻底吃透这类“树上覆盖”问题的通用解法。

题目

给定一棵二叉树,在树的节点上安装摄像头。每个节点上的摄像头都可以监视其父节点、自身以及它的直接子节点。计算监控树中所有节点所需的最少摄像头数量。

示例 1:

输入:[0,0,null,0,0] 输出:1 解释:如图所示放置一个摄像头即可监控所有节点。

示例 2:

输入:[0,0,null,0,null,0,null,null,0] 输出:2 解释:至少需要两个摄像头才能监控树的所有节点。上图展示了其中一种合法的摄像头放置方案。

注意:

  1. 给定树的节点数范围为[1, 1000]
  2. 每个节点的值都是 0(节点值本身对解题没有影响,仅为占位)。

题目大意

给定一个二叉树,我们在树的节点上安装摄像头。节点上的每个摄像头都可以监视其父对象、自身及其直接子对象。计算监控树的所有节点所需的最小摄像头数量。

提示:

  1. 给定树的节点数的范围是 [1, 1000]。
  2. 每个节点的值都是 0。

解题思路

核心思想:贪心 + 节点三分类

给出一棵树,要求在这棵树上放置摄像头,一个摄像头最多可以监视 4 个节点:2 个孩子节点、节点本身、还有父节点。问最少放多少个摄像头可以覆盖树上的所有节点。

这一题可以用贪心思想来解。先将节点分为 3 类:

  • 第一类(叶子节点,状态 0):没有任何摄像头覆盖到,需要被父节点的摄像头覆盖,或者自己放摄像头;
  • 第二类(包含叶子节点的节点,状态 1):是某个放摄像头的节点的“父节点”,即它自己放了摄像头;
  • 第三类(其中一个孩子已放摄像头、自身已被覆盖的节点,状态 2):自身已被孩子的摄像头覆盖,不需要再放摄像头。

按照这个想法,将树的每个节点染色,如下图所示(图片出自原题解文档,用于直观展示贪心染色过程)。

贪心策略:从最底层叶子节点往上“染色”

所有包含叶子节点的节点,可以放一个摄像头,这个摄像头可以覆盖至少 3 个节点;如果还有父节点的话,可以覆盖 4 个节点。所以贪心的策略是从最下层的叶子节点开始往上“染色”

  1. 先把最下面一层的叶子节点染成 1——标 1 的节点都是要放一个摄像头的
  2. 如果某节点的孩子中包含 1(放了摄像头),那么再将该节点染成 2。如下图中的黄色节点——黄色节点代表不用放摄像头的节点,因为它已经被叶子节点的摄像头覆盖了;
  3. 出现了 2 的节点以后,再往上的节点又再次恢复成“叶子节点”0,需要继续被上层覆盖;
  4. 如此类推,直到推到根节点。

根节点收尾的边界情况

最后根节点还需要注意多种情况:

  • 根节点可能是叶子节点 0,那么最终答案还需要+1,因为需要在根节点上放一个摄像头,否则根节点覆盖不到;
  • 根节点也有可能是1 或者 2,这两种情况都不需要增加摄像头了,因为都已经覆盖到了。

按照上述方法,递归即可得到答案。

仓库源码逐行解析

仓库中该题目的完整实现位于 968. Binary Tree Cameras.go,文件开头的注释给出了 LeetCode 标准的二叉树节点定义(TreeNode类型通过 structures/TreeNode.go 复用):

/** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */

三状态定义

实现中用自定义类型status表达了上面解题思路里的三分类:

type status int const ( isLeaf status = iota // 0:叶子节点(未被覆盖,需要被上层照顾) parentofLeaf // 1:放了摄像头的节点 isMonitoredWithoutCamera // 2:已被孩子摄像头覆盖、无需再放摄像头 )

三个常量值分别对应 0、1、2,与解题思路中的染色编号完全一致。

主函数与递归

func minCameraCover(root *TreeNode) int { res := 0 if minCameraCoverDFS(root, &res) == isLeaf { res++ } return res } func minCameraCoverDFS(root *TreeNode, res *int) status { if root == nil { return 2 } left, right := minCameraCoverDFS(root.Left, res), minCameraCoverDFS(root.Right, res) if left == isLeaf || right == isLeaf { *res++ return parentofLeaf } else if left == parentofLeaf || right == parentofLeaf { return isMonitoredWithoutCamera } else { return isLeaf } }

递归逻辑对照贪心染色

  • 空节点返回 2nil孩子相当于一个“已被覆盖、不需要摄像头”的节点,不会影响父节点的决策。注意这里2是数字字面量,等价于isMonitoredWithoutCamera
  • 孩子中有 0(叶子):说明当前节点是“包含叶子节点的节点”,按贪心策略应当放一个摄像头res++),并向上返回1parentofLeaf)。
  • 孩子中有 1(放了摄像头):当前节点已被孩子覆盖,不需要放摄像头,返回2isMonitoredWithoutCamera)。
  • 否则(两个孩子都是 2):当前节点没有任何覆盖来源,只能“寄希望于父节点”,向上返回0isLeaf)。
  • 根节点收尾:递归结束后若根节点返回0(叶子状态,没有被任何摄像头覆盖),则res++,在根节点补放一个摄像头。

该实现是一个典型的后序遍历(post-order DFS):先递归处理左右子树,再根据两个孩子的状态决定当前节点的状态,正好对应“自底向上染色”的过程。每个节点只访问一次,空间复杂度为树高 O(H)。

单元测试与运行

仓库内配套测试位于 968. Binary Tree Cameras_test.go,覆盖了题目给出的两个官方示例,并额外补充了单节点树的边界用例:

输入(层序数组,structures.NULL表示空)期望输出用例类型
[0,0,NULL,0,0]1官方示例 1
[0,0,NULL,0,NULL,0,NULL,NULL,0]2官方示例 2
[0]1单节点边界

测试将层序数组通过 structures.Ints2TreeNode 构建二叉树后调用minCameraCover,并断言结果。structures.NULL定义于 structures/TreeNode.go,值为-1 << 63,用于在测试数据中表示空节点。

运行该用例(在仓库根目录执行):

go test -v -run Test_Problem968 ./leetcode/0968.Binary-Tree-Cameras/

输出示例:

------------------------Leetcode Problem 968------------------------ 【input】:[0 0 -9223372036854775808 0 0] 【output】:1 【input】:[0 0 -9223372036854775808 0 -9223372036854775808 0 -9223372036854775808 -9223372036854775808 0] 【output】:2 【input】:[0] 【output】:1

提示:打印出来的-9223372036854775808即为structures.NULL的真实数值(-1 << 63)。

若要跑全仓库测试并生成覆盖率报告,可参考仓库根目录的 gotest.sh:

go test -covermode=atomic -coverprofile=coverage.txt ./leetcode/...

复杂度分析

  • 时间复杂度:O(N),N 为节点总数。后序遍历每个节点恰好访问一次,每个节点的决策均为 O(1)。
  • 空间复杂度:O(H),H 为树的高度。递归调用栈深度最坏情况下为 O(N)(退化成链状树),最好情况下为 O(log N)(平衡树)。

小结

968 题的核心套路可以总结为一句:后序遍历 + 三状态贪心。用 0/1/2 三个状态表达“需要被覆盖 / 自己放摄像头 / 已被覆盖”,自底向上决策,最后单独处理根节点。理解这一题后,同一套路也可迁移到其他“树上最小覆盖”类问题(如监控叶子、覆盖边等变体),是学习树形 DP 与贪心结合的良好入门案例。

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 13:09:17

个人微信二次开发还能实现哪些小功能?从接口文档发现实用玩法

主流功能&#xff08;收发消息、联系人、群管理&#xff09;之外&#xff0c;接口文档里还有一批"小接口"——单独看不起眼&#xff0c;组合到业务里能解决具体问题。 一、消息已读状态查询 发送消息后可以查询消息的送达/已读状态。用途不是"监控客户看没看&…

作者头像 李华
网站建设 2026/9/12 13:09:09

ETC门架机房温湿度智能预警方案:云边协同+本地自治

1. 项目概述&#xff1a;为什么ETC门架机房的温湿度不能只靠“看一眼”高速公路上那些立在龙门架上的ETC门架系统&#xff0c;不是装上就完事的摆设。我干这行十多年&#xff0c;跑过全国二十多个省的高速机电养护现场&#xff0c;最常听到的一句话是&#xff1a;“门架没电了”…

作者头像 李华
网站建设 2026/9/12 13:08:16

项目管理系统选型指南:按项目类型匹配功能,避免落地失败

做了这么多年项目管理相关的选型咨询&#xff0c;我最怕听到的一句话就是“选一套好的项目管理系统&#xff0c;大家都能用”。说这话的人通常已经踩过坑了——同一个软件&#xff0c;放在软件研发团队顺风顺水&#xff0c;流转到市场部用了一个月就荒废了&#xff1b;销售团队…

作者头像 李华
网站建设 2026/9/12 13:07:26

图数据结构与算法:从基础实现到工程优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华