leetcode
leetcode 851 ~ 900
监控二叉树

监控二叉树

难度:

标签:

题目描述

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

 

示例 1:

输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。

示例 2:

输入:[0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。


提示:

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

代码结果

运行时间: 24 ms, 内存: 16.3 MB


/* 
 题目思路:
 - 与 Java 实现类似,但我们将尝试使用 Java Streams 进行处理。
 - 然而,由于树的节点操作通常涉及递归或队列的逐层处理,Java Streams 可能不适用。
 - 因此,本方案仍然会使用递归实现 DFS。 
 */

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    private int cameras = 0;

    public int minCameraCover(TreeNode root) {
        if (dfs(root) == 0) cameras++;
        return cameras;
    }

    private int dfs(TreeNode node) {
        if (node == null) return 2;
        int left = dfs(node.left);
        int right = dfs(node.right);
        if (left == 0 || right == 0) {
            cameras++;
            return 1;
        }
        return (left == 1 || right == 1) ? 2 : 0;
    }
}

解释

方法:

题解采用了递归的方法,利用后序遍历(先处理子节点,再处理当前节点)的方式来决定在哪些节点上安装摄像头。递归函数返回三种状态:0 表示当前节点未被覆盖,1 表示当前节点已安装摄像头,2 表示当前节点已被覆盖但未安装摄像头。根据子节点的状态来决定当前节点的操作: 1. 如果任一子节点未被覆盖(返回0),则在当前节点安装摄像头,并返回状态1。 2. 如果所有子节点都已被覆盖(返回2),则当前节点返回状态0。 3. 如果任一子节点安装了摄像头(返回1),则当前节点返回状态2,表示已被覆盖。 最后,若根节点在递归后仍未被覆盖,则在根节点额外安装一个摄像头。

时间复杂度:

O(n)

空间复杂度:

O(n) in the worst case, O(log(n)) in the best case

代码细节讲解

🦆
在递归函数中,如何判断一个节点是否已经被覆盖?
在递归函数中,一个节点是否已经被覆盖是通过其返回的状态值来判断的。如果一个节点返回状态1,说明在该节点上已经安装了摄像头,从而该节点及其子树均被覆盖;如果返回状态2,说明该节点已经被覆盖但未安装摄像头,这通常是因为它的某个子节点上安装了摄像头。如果返回状态0,这表示该节点未被覆盖,需要在其父节点或者该节点本身安装摄像头来确保覆盖。
🦆
为什么最后还需要检查根节点是否被覆盖,根据递归逻辑,根节点不是应该总是被覆盖吗?
尽管递归处理过程中设计了多种情况以确保大部分节点被覆盖,但还是存在一种情况:如果根节点的左右子节点都返回状态2(即它们都已被覆盖但未安装摄像头),根据递归逻辑,根节点本身会返回状态0(未被覆盖)。这是因为状态2的子节点假定它们的父节点或上方某处已被安装了摄像头。因此,需要在递归结束后检查根节点的状态,如果是0,说明需要在根节点额外安装一个摄像头来确保整个树被覆盖。
🦆
递归终止条件中,为什么不存在的节点(cur为None)被认为是已经覆盖(返回状态2)?
在递归终止条件中,不存在的节点(即cur为None的情况)被认为是已经覆盖(返回状态2),是因为不存在的节点不需要监控。这种设计简化了递归逻辑,免去了在递归过程中对空节点进行额外处理的需要。当一个节点的子节点为None时,可以认为这个子节点已经安全,不需要额外的摄像头覆盖。这使得算法可以专注于处理实际存在的节点。
🦆
请解释为什么当左右子节点均返回状态2时,当前节点的状态返回0(未被覆盖)?
当一个节点的左右子节点均返回状态2时,意味着这两个子节点都已被覆盖但它们自身并未安装摄像头。在这种情况下,这两个子节点的覆盖是由它们各自的子节点或更下层的节点上的摄像头保证的。因此,当前节点(父节点)本身并没有直接被任何摄像头覆盖,也没有其子节点在其上安装摄像头来提供覆盖。所以,当前节点的状态返回0,表示它未被覆盖,需要在其上或更高层节点安装摄像头以确保覆盖。

相关问题

在二叉树中分配硬币

给你一个有 n 个结点的二叉树的根结点 root ,其中树中每个结点 node 都对应有 node.val 枚硬币。整棵树上一共有 n 枚硬币。

在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。移动可以是从父结点到子结点,或者从子结点移动到父结点。

返回使每个结点上 只有 一枚硬币所需的 最少 移动次数。

 

示例 1:

输入:root = [3,0,0]
输出:2
解释:一枚硬币从根结点移动到左子结点,一枚硬币从根结点移动到右子结点。

示例 2:

输入:root = [0,3,0]
输出:3
解释:将两枚硬币从根结点的左子结点移动到根结点(两次移动)。然后,将一枚硬币从根结点移动到右子结点。

 

提示:

  • 树中节点的数目为 n
  • 1 <= n <= 100
  • 0 <= Node.val <= n
  • 所有 Node.val 的值之和是 n