968. 监控二叉树

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

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

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

示例 1:

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

示例 2:

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

提示:

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

java代码

这是一道hard难度的题目。我们要从底向上进行推导,因为尽量让叶子节点的父节点安装摄像头,这样摄像头的数量才是最少的。 如何从低向上推导呢? 采用后序遍历也就是左右中的顺序,这样就可以从下到上进行推导了。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    //NO_CAMERA表示的是子节点没有相机,当前节点也没放相机
    private final int NO_CAMERA = 0;
    //HAS_CAMERA表示当前节点有一个相机
    private final int HAS_CAMERA = 1;
    //NO_NEEDED表示当前节点没有相机,但他的子节点有一个相机,把它给
    //覆盖了,所以它不需要了。或者他是一个空的节点也是不需要相机的
    private final int NO_NEEDED = 2;

    //全局的,统计有多少相机
    int res = 0;

    public int minCameraCover(TreeNode root) {
        //边界条件判断
        if (root == null)
            return 0;
        //如果最后返回的是NO_CAMERA,表示root节点的子节点也没有相机,
        //所以root节点要添加一个相机
        if (dfs(root) == NO_CAMERA)
            res++;
        //返回结果
        return res;
    }

    public int dfs(TreeNode root) {
        //如果是空的,就不需要相机了
        if (root == null)
            return NO_NEEDED;
        int left = dfs(root.left);
        int right = dfs(root.right);
        //如果左右子节点有一个是NO_CAMERA,表示的是子节点既没相机,也没相机覆盖它,
        //所以当前节点需要有一个相机
        if (left == NO_CAMERA || right == NO_CAMERA) {
            //在当前节点放一个相机,统计相机的个数
            res++;
            return HAS_CAMERA;
        }
        //如果左右子节点只要有一个有相机,那么当前节点就不需要相机了,否则返回一个没有相机的标记
        return (left == HAS_CAMERA || right == HAS_CAMERA) ? NO_NEEDED : NO_CAMERA;
    }

}

cpp代码

状态

  • 0:该节点无覆盖,(说明,左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。)
  • 1:本节点有摄像头
  • 2:本节点有覆盖
class Solution {
private:
    int result;
    int traversal(TreeNode* cur) {

        // 空节点,该节点有覆盖
        if (cur == NULL) return 2;

        int left = traversal(cur->left);    // 左
        int right = traversal(cur->right);  // 右
        
        //先考虑左右节点,再考虑中间节点,所以采用后序遍历

        // 情况1 - - - - - - 左右节点都有覆盖
        // 左右节点都有覆盖
        if (left == 2 && right == 2) return 0;

        // 情况2 - - - - - - - - - - - -- - - - 左右节点至少有一个无覆盖的情况
        // left == 0 && right == 0 左右节点无覆盖
        // left == 1 && right == 0 左节点有摄像头,右节点无覆盖
        // left == 0 && right == 1 左节点有无覆盖,右节点摄像头
        // left == 0 && right == 2 左节点无覆盖,右节点覆盖
        // left == 2 && right == 0 左节点覆盖,右节点无覆盖
        if (left == 0 || right == 0) {
            result++;
            return 1;
        }

        // 情况3 - - - - - - - - - - - - - - - - - - - - 左右节点至少有一个有摄像头
        // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        // left == 1 && right == 1 左右节点都有摄像头
        // 其他情况前段代码均已覆盖
        if (left == 1 || right == 1) return 2;

        // 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
        // 这个 return -1 逻辑不会走到这里。
        return -1;
    }

public:
    int minCameraCover(TreeNode* root) {
        result = 0;
        // 情况4
        if (traversal(root) == 0) { // root 无覆盖
            result++;
        }
        return result;
    }
};

参考资料

监控二叉树

968. 监控二叉树:【递归上的状态转移】详解

从下往上计算,击败了100%的用户