训练营第三十四天贪心(完结)

文章讲述了如何使用贪心算法解决编程问题,包括判断给定整数是否为单调递增的数字以及在二叉树上安装摄像头以监控所有节点的最小数量。作者提供了两种解决方案,一种针对单调递增整数,另一种涉及二叉树的后序遍历策略。
摘要由CSDN通过智能技术生成

训练营第三十四天贪心(完结)

738.单调递增的数字

力扣题目链接

题目

当且仅当每个相邻位数上的数字 xy 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增

示例 1:

输入: n = 10
输出: 9

示例 2:

输入: n = 1234
输出: 1234

示例 3:

输入: n = 332
输出: 299

提示:

  • 0 <= n <= 109

解答

从右向左遍历,如果前一个比后一个大,就把前一个-1,后一个变成9

例:52 -》 49

错误解法

虽然大体思路如此,但是忽略了特殊情况

例:100 -》 如果相等,那么就会变成090即90而不是99,所以只要发生了调换,就应该从调换位置开始将后面所有的都变成9

class Solution {
		//101
		//100
    public int monotoneIncreasingDigits(int n) {
		String temp = String.valueOf(n);//先转为字符串
		char[] nums = temp.toCharArray();//再将每一位转为char数组
		for (int i = nums.length - 1; i > 0; i--) {
			if (nums[i - 1] > nums[i]){
				nums[i] = '9';
				nums[i - 1] -= 1;
			}
		}
		return Integer.parseInt(String.valueOf(nums));
    }
}
正确解法
class Solution {
    public int monotoneIncreasingDigits(int n) {
		String temp = String.valueOf(n);//先转为字符串
		char[] nums = temp.toCharArray();//再将每一位转为char数组
		int index = nums.length;
		for (int i = nums.length - 1; i > 0; i--) {
			if (nums[i - 1] > nums[i]) {
				nums[i - 1] -= 1;
				index = i;
			}
		}
		for (int i = index; i < nums.length; i++) {
			nums[i] = '9';
		}
		return Integer.parseInt(String.valueOf(nums));
    }
}

968.监控二叉树

力扣题目链接

题目

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

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

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

示例 1:

在这里插入图片描述

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

示例 2:

在这里插入图片描述

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

提示:

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

解答

局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!

从低到上,先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。

后序遍历

  • 0:该节点无覆盖
  • 1:本节点有摄像头
  • 2:本节点有覆盖
情况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 左节点覆盖,右节点无覆盖

这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。

此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。

代码如下:

if (left == 0 || right == 0) {
    result++;
    return 1;
}
情况3:左右节点至少有一个有摄像头

如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)

  • left == 1 && right == 2 左节点有摄像头,右节点有覆盖
  • left == 2 && right == 1 左节点有覆盖,右节点有摄像头
  • left == 1 && right == 1 左右节点都有摄像头

代码如下:

if (left == 1 || right == 1) return 2;

从这个代码中,可以看出,如果left == 1, right == 0 怎么办?其实这种条件在情况2中已经判断过了

在这里插入图片描述

情况4:头结点没有覆盖

以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况,如图:

在这里插入图片描述

所以递归结束之后,还要判断根节点,如果没有覆盖,result++,代码如下:

int minCameraCover(TreeNode* root) {
    result = 0;
    if (traversal(root) == 0) { // root 无覆盖
        result++;
    }
    return result;
}
class Solution {
	int  res=0;
	public int minCameraCover(TreeNode root) {
		// 对根节点的状态做检验,防止根节点是无覆盖状态 .
		if(minCame(root)==0){
			res++;
		}
		return res;
	}
	/**
	 节点的状态值:
	 0 表示无覆盖
	 1 表示 有摄像头
	 2 表示有覆盖
	 后序遍历,根据左右节点的情况,来判读 自己的状态
	 */
	public int minCame(TreeNode root){
		if(root==null){
			// 空节点默认为 有覆盖状态,避免在叶子节点上放摄像头
			return 2;
		}
		int left=minCame(root.left);
		int  right=minCame(root.right);

		// 如果左右节点都覆盖了的话, 那么本节点的状态就应该是无覆盖,没有摄像头
		if(left==2&&right==2){
			//(2,2)
			return 0;
		}else if(left==0||right==0){
			// 左右节点都是无覆盖状态,那 根节点此时应该放一个摄像头
			// (0,0) (0,1) (0,2) (1,0) (2,0)
			// 状态值为 1 摄像头数 ++;
			res++;
			return 1;
		}else{
			// 左右节点的 状态为 (1,1) (1,2) (2,1) 也就是左右节点至少存在 1个摄像头,
			// 那么本节点就是处于被覆盖状态
			return 2;
		}
	}
}

简化分支

class Solution {
    static int ans;
    public int minCameraCover(TreeNode root) {
        ans = 0; // 初始化
        if(f(root) == 0) ans ++;
        return ans;
    }
    // 定义 f 函数有三种返回值情况
    // 0:表示 x 节点没有被相机监控,只能依靠父节点放相机
    // 1:表示 x 节点被相机监控,但相机不是放在自身节点上
    // 2:表示 x 节点被相机监控,但相机放在自身节点上
    public static int f(TreeNode x) {
        if(x == null) return 1; // 空树认为被监控,但没有相机
        // 左右递归到最深处
        int l = f(x.left);
        int r = f(x.right);
        // 有任意一个子节点为空,就需要当前节点放相机,不然以后没机会
        if(l == 0 || r == 0) {
            ans ++; // 放相机
            return 2;
        }
        // 贪心策略,左右子树都被监控,且没有监控到当前节点,
        // 那么最有利的情况就是将相机放置在当前节点父节点上,
        // 因为这样能多监控可能的子树节点和父父节点
        if(l == 1 && r == 1) return 0;
        // 剩下情况就是左右子树有可能为 2,即当前节点被监控
        return 1; 
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值