LeetCode 968. 监控二叉树
题目
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
示例 1:
输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。
示例 2:
输入:[0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。
提示:
- 给定树的节点数的范围是 [1, 1000]。
- 每个节点的值都是 0。
思路
1. 贪心算法
我们可以用贪心的思想来解决这个问题。
- 因为要求最少的摄像头数量,所以我们应该尽可能的让摄像头监控到更多的节点。
- 所以叶子结点不能放置摄像头,因为在叶子结点放置摄像头,只能监控到叶子结点本身和它的父节点,而不能监控到它的子节点,
浪费了摄像头的监控范围。 - 所以我们应该从叶子结点开始,向上安装摄像头,这样可以让摄像头监控到更多的节点。
- 每个结点有三种状态,0 表示该结点待覆盖,1 表示该结点已经覆盖,2 表示该结点上安装了摄像头。
- 我们可以用后序遍历的方式,从叶子结点开始,向上安装摄像头,如果当前结点的左右子节点有一个为 0,那么当前结点就需要安装摄像头,
并且当前结点的状态为 2,如果当前结点的左右子节点有一个为 2,那么当前结点的状态为 1,如果当前结点的左右子节点有一个为 1,
那么当前结点的状态为 0。 - 最后,如果根节点的状态为 0,那么根节点也需要安装摄像头,如果根节点的状态为 2,那么根节点不需要安装摄像头,如果根节点的状态为
1,
那么根节点的状态为 0。 - 最后,我们只需要统计出状态为 2 的结点数量,就是我们需要安装的摄像头的数量。
实现
java
public class Solution {
int res = 0;
public int minCameraCover(TreeNode root) {
if (root == null) {
return 0;
}
if (dfs(root) == 0) {
res++;
}
return res;
}
private int dfs(TreeNode root) {
if (root == null) {
return 1;
}
int left = dfs(root.left);
int right = dfs(root.right);
if (left == 0 || right == 0) {
res++;
return 2;
}
if (left == 2 || right == 2) {
return 1;
}
return 0;
}
}
复杂度分析
- 时间复杂度:O(n),n 为二叉树的节点数,需要遍历所有的节点。
- 空间复杂度:O(n),n 为二叉树的节点数,递归栈的深度最大为 n。
python
class Solution:
def minCameraCover(self, root: TreeNode) -> int:
self.res = 0
def dfs(root):
if not root:
return 1
left = dfs(root.left)
right = dfs(root.right)
if left == 0 or right == 0:
self.res += 1
return 2
if left == 2 or right == 2:
return 1
return 0
if dfs(root) == 0:
self.res += 1
return self.res
LeetCode 738. 单调递增的数字
题目
给定一个非负整数 N
,找出小于或等于 N
的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。
(当且仅当每个相邻位数上的数字 x
和 y
满足 x <= y
时,我们称这个整数是单调递增的。)
示例 1:
输入: N = 10
输出: 9
示例 2:
输入: N = 1234
输出: 1234
示例 3:
输入: N = 332
输出: 299
说明: N
是在 [0, 10^9]
范围内的一个整数。
思路
1. 暴力
- 我们可以从
N
开始,依次判断每个数是否满足题目要求,如果满足,那么就返回当前的数。 - 如果不满足,那么我们就继续判断下一个数,直到找到满足题目要求的数为止。
java
class Solution {
public int monotoneIncreasingDigits(int N) {
for (int i = N; i >= 0; i--) {
if (isMonotoneIncreasingDigits(i)) {
return i;
}
}
return 0;
}
private boolean isMonotoneIncreasingDigits(int N) {
char[] chars = String.valueOf(N).toCharArray();
for (int i = 1; i < chars.length; i++) {
if (chars[i - 1] > chars[i]) {
return false;
}
}
return true;
}
}
2. 贪心算法
我们可以用贪心的思想来解决这个问题。
- 我们从高位向低位遍历,如果当前位的数字大于下一位的数字,那么我们就把当前位的数字减 1,然后把当前位后面的所有位都变成 9。
- 如果当前位的数字小于下一位的数字,那么我们就不做任何处理。
- 最后,我们只需要把处理后的数字转换成整数即可。
java
class Solution {
public int monotoneIncreasingDigits(int N) {
char[] chars = String.valueOf(N).toCharArray();
int i = 1;
while (i < chars.length && chars[i - 1] <= chars[i]) {
i++;
}
if (i < chars.length) {
while (i > 0 && chars[i - 1] > chars[i]) {
chars[i - 1]--;
i--;
}
for (i += 1; i < chars.length; i++) {
chars[i] = '9';
}
}
return Integer.parseInt(new String(chars));
}
}
复杂度分析
- 时间复杂度:O(logN)。我们需要遍历数字 N 的每一位,一共有 O(logN) 位。
- 空间复杂度:O(logN)。我们需要 O(logN) 的空间来存储数字 N 的每一位。
python
class Solution:
def monotoneIncreasingDigits(self, N: int) -> int:
chars = list(str(N))
i = 1
while i < len(chars) and chars[i - 1] <= chars[i]:
i += 1
if i < len(chars):
while i > 0 and chars[i - 1] > chars[i]:
chars[i - 1] = str(int(chars[i - 1]) - 1)
i -= 1
for j in range(i + 1, len(chars)):
chars[j] = '9'
return int(''.join(chars))