系列文章目录
738.单调递增的数字
贪心算法
从后往前遍历,如果前一个数字大于后一个数字chars[i] > chars[i + 1]
,则后一个数字变为9
,前一个数字-1
。
- 从后往前遍历,是因为只有从后向前遍历才能重复利用上次比较的结果。因前一个数字
-1
,即chars[i]--;
,此时i
位置上的数字有可能比i-1
上数字小,如果从前往后遍历,如数字332
,从前向后遍历的话,那么就变成了329
,此时2
又小于了第一位的3
了,无法得到真正的结果299
。而从后向前遍历,就可以重复利用上次比较得出的结果了,从后向前遍历332
的数值变化为:332 -> 329 -> 299
- 注:不能遇到前一个数字大于后一个数字
chars[i] > chars[i + 1]
,就把后一个数字变为9
,前一个数字-1
。如果这样写会出现:2100->2090->1990
,而正确答案应该是1999
。 - 而应该只将前一个数字
-1
,同时记录后一个数字的位置来标记从哪里开始赋值9
。
class Solution {
public int monotoneIncreasingDigits(int n) {
String s = String.valueOf(n);
char[] chars = s.toCharArray();
int start = chars.length;
for (int i = chars.length - 2; i >= 0; i--) {
if (chars[i] > chars[i + 1]) {
chars[i]--;//得到的对应的ASCII 值
//chars[i] = (Integer.parseInt(chars[i]) - 1) + "";
start = i + 1;
}
}
for (int i = start; i < chars.length; i++) {
chars[i] = '9';
}
return Integer.parseInt(String.valueOf(chars));
}
}
968.监控二叉树(困难题)
贪心算法
- 从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!局部最优推出全局最优。
- 大体思路就是从低到上,先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。
- 此时这道题目有两个难点:①二叉树的遍历。②如何隔两个节点放一个摄像头。
- ①确定遍历顺序:使用后序遍历也就是左右中的顺序,这样就可以在回溯的过程中从下到上进行推导了。
注意在以上代码中我们取了左孩子的返回值,右孩子的返回值,即int traversal(TreeNode* cur) { // 空节点,该节点有覆盖 if (终止条件) return ; int left = traversal(cur->left); // 左 int right = traversal(cur->right); // 右 逻辑处理 // 中 return ; }
left
和right
, 以后推导中间节点的状态。 - ②如何隔两个节点放一个摄像头:
此时需要状态转移的公式,大家不要和动态的状态转移公式混到一起,本题状态转移没有择优的过程,就是单纯的状态转移!
每个节点有如下三种状态:
状态 0:该节点无覆盖;
状态 1:本节点有摄像头;
状态 2:本节点有覆盖;
(本节点无摄像头,其实无摄像头就是 无覆盖 或者 有覆盖的状态)
因为在遍历树的过程中,就会遇到空节点,空节点不能是无覆盖的状态,这样叶子节点就要放摄像头了,空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头了,而是可以把摄像头放在叶子节点的爷爷节点上。所以空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了。
- ①确定遍历顺序:使用后序遍历也就是左右中的顺序,这样就可以在回溯的过程中从下到上进行推导了。
- 递推关系:
-
递归的终止条件:遇到空节点,返回
2
(有覆盖) 。 -
单层逻辑处理:
(子节点状态, 子节点状态) -> 当前节点状态(0, 0) -> 1 (0, 1) -> 1 (0, 2) -> 1 (1, 0) -> 1 (2, 0) -> 1 (1, 1) -> 2 (1, 2) -> 2 (2, 1) -> 2 (2, 2) -> 0
①情况1:左右节点至少有一个无覆盖的情况;有一个孩子没有覆盖,父节点就应该放摄像头。此时摄像头的数量要
+1
,并且return 1
,代表中间节点放摄像头。
②情况2:左右节点至少有一个有摄像头;其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2
(覆盖的状态)。
③情况3:左右节点都有覆盖;左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。
④情况4:头结点没有覆盖;递归结束之后,还要判断根节点,如果没有覆盖,摄像头的数量要+1
。
-
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
//后序遍历-左右中(从下往上遍历)
class Solution {
int res;
public int minCameraCover(TreeNode root) {
//递归后判断根节点的状态,如果为未覆盖,则需加一个摄像头,防止根节点是无覆盖状态
if (minCamera(root) == 0) {
res++;
}
return res;
}
/*
节点的状态值:
0:表示无覆盖
1:表示有摄像头
2:表示有覆盖
后序遍历,根据左右节点的情况,来判读自己的状态
*/
public int minCamera(TreeNode node) {
//终止条件,空节点默认为 有覆盖状态,避免在叶子节点上放摄像头
if (node == null) {
return 2;
}
int left = minCamera(node.left);//左
int right = minCamera(node.right);//右
//中
if (left == 0 || right == 0) {//左右节点至少有一个没覆盖,那本节点此时应该放一个摄像头
res++;//添加一个摄像头
return 1;
} else if (left == 1 || right == 1) return 2;//左右节点至少有一个摄像头,那么本节点就是处于被覆盖状态
else return 0;//左右节点都已经被覆盖, 那么本节点的状态就应该是无覆盖,没有摄像头
}
}