leetcode刷题:贪心算法18(监控二叉树)

968.监控二叉树

力扣题目链接

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

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

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

示例 1:

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

示例 2:

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

提示:

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

贪心思路:构造监控二叉树
首先要知道监控结点该怎么放置。经过观察发现,所有的叶子结点的父结点必定为监控结点。以此为切入点,所有的父结点都要根据子结点进行构造。这就必须用到后序遍历:左右根。

如果当前结点为空节点,返回3。
如果无子节点,则当前结点是叶子结点,标记值为2。
如果有一个子节点是2,则当前结点必定为监控结点,标记为1。
如果有一个子节点为监控结点,一个结点为空,则当前结点标记为伪叶子结点2。
如果左右子节点都为监控结点。则当前结点为非监控结点,标记为0。
如果左右子节点一个为监控结点,另一个为空,则当前结点为非监控结点,标记为0。

最后遍历完后,再判断根结点的情况,根结点根据实际情况要单独处理
如果根结点的子节点一个为空,另一个为非监控结点,则根结点必须为监控结点,专门用来监控自己,标记1。
如果根结点的子节点两个都为非监控结点,则根结点必须为监控结点,专门用来监控自己,标记为1。
如果根结点是无子结点,则根结点必须为监控结点,专门用来监控自己,标记为1。

package com.programmercarl.greed;

import com.programmercarl.tree.TreeNode;
import com.programmercarl.util.GenerateTreeNode;


/**
 * @ClassName MinCameraCover
 * @Descriotion TODO
 * @Author nitaotao
 * @Date 2022/7/18 22:19
 * @Version 1.0
 * https://leetcode.cn/problems/binary-tree-cameras/
 * 968. 监控二叉树
 **/
public class MinCameraCover {
    //监控点数量
    int count = 0;

    public int minCameraCover(TreeNode root) {
        if (root.left == null && root.right == null) {
            return 1;
        }
        /**
         * 每个叶子结点的上面结点必是监控
         * 监控结点上面的结点必为0
         * 可以把监控结点视为另类叶子结点
         * 需要从下往上遍历
         * 先序遍历 根左右
         * 中序遍历 左中右
         * 后续遍历 左右中
         *
         * 遇到叶子结点,赋值为2,标记位叶子结点
         * 其父结点赋值为1 , 标记为监控结点
         * 遇到监控结点,其父结点赋值为2,标记为非监控结点
         * 最后遍历。如果其结点值为1,即为监控结点,sun++
         */
        //标记结点
        int rootVal = flagTraversal(root);
        //如果一个为非监控结点,另一个为空
        if ((root.left == null && root.right.val == 0) || (root.right == null && root.left.val == 0)) {
            root.val = 1;
        }

        //如果两个都为非监控结点
        if (root.left != null && root.left.val == 0 && root.right != null && root.right.val == 0) {
            root.val = 1;
        }

        //如果一个叶子一个是

        //计算总和
        getCameraNumber(root);
        return count;
    }

    /**
     * 贪心思想
     * <p>
     * 标记结点
     * 遇到叶子结点,赋值为2,标记位叶子结点
     * 其父结点赋值为1 , 标记为监控结点
     * 遇到监控结点,其父结点赋值为2,标记为非监控结点
     *
     * @param node 需要根据子节点的值进行监控结点的标记
     *             左右根
     *             后序遍历
     */
    public int flagTraversal(TreeNode node) {
        //叶子结点标记为2
        //null标记为3
        //监控结点标记为1
        //非监控结点标记为0
        /**
         *         1
         *       2   3
         *     0  3
         *   1  3
         * 3  2
         *   3  3
         *
         */

        //贪心思路:如果左右有一个为null,并且另一个为监控结点,当前结点则为

        if (node == null) {
            // 3为空结点
            return 3;
        }
        //左子树
        int left = flagTraversal(node.left);

        //右子树
        int right = flagTraversal(node.right);

        //如果没有子节点,当前结点即为叶子结点,标记为 2
        //所有的叶子结点
        if (left == 3 && right == 3) {
            node.val = 2;
        }

        //如果左右有一个为叶子结点,当前结点必为监控结点
        if (left == 2 || right == 2) {
            //当前结点设置为监控结点
            node.val = 1;
        }
        //如果一个为非监控结点,另一个为空。或者两个都是非监控结点
        if ((left == 0 && right == 3) || (left == 3 && right == 0) || (left == 0 && right == 0)) {
            //当前结点根据贪心思路,设置为伪叶子结点
            node.val = 2;
        }


        //如果左右都为监控结点
        if (left == 1 && right == 1) {
            //当前结点设为非叶子结点
            node.val = 0;
        }

        //如果只有一个为监控结点,另一个为空
        //非监控结点
        if ((left == 1 && right == 3) || (left == 3 && right == 1)) {
            node.val = 0;
        }

        //返回当前结点的值
        return node.val;
    }

    /**
     * 递归,中序遍历结点获取监控点数量
     *
     * @param node
     */
    public void getCameraNumber(TreeNode node) {
        if (node == null) {
            return;
        }
        getCameraNumber(node.left);
        if (node.val == 1) {
            count++;
        }
        getCameraNumber(node.right);
    }

    public static void main(String[] args) {
        System.out.println(new MinCameraCover().minCameraCover(GenerateTreeNode.generateTreeNode("[0,0,0,null,0,0,null,null,0,null,0,null,0,null,null,0,null,0,0]")));
        System.out.println(new MinCameraCover().minCameraCover(GenerateTreeNode.generateTreeNode("[0,null,0,null,0,null,0]")));
        System.out.println(new MinCameraCover().minCameraCover(GenerateTreeNode.generateTreeNode("[0,0,null,0,0,0,null,null,0]")));
    }
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值