贪心算法 Part05
合并区间
题目描述: 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
思路:对于这道题目,我们只需要按照每个区间的左边界进行从小到大排序,随后依次遍历每个区间,获取他们的范围,将范围有重叠的区间合并(取重叠区间的最小做区间和最大右区间的值),并将合并后得出的每个区间的范围重新记录,最后输出即可。
代码如下:
java
class Solution {
public int[][] merge(int[][] intervals) {
// Arrays.sort(intervals,(a,b) -> a[0] - b[0]);
Arrays.sort(intervals,(a,b) -> Integer.compare(a[0],b[0]));
int start = intervals[0][0] ;
int end = intervals[0][1];
List<int[]> res = new LinkedList<>();
for(int i = 1 ; i < intervals.length ; i++){
if(intervals[i][0] <= end){
start = Math.min(start , intervals[i][0]);
end = Math.max(end , intervals[i][1]);
}else{
res.add(new int[]{start , end});
start = intervals[i][0];
end = intervals[i][1];
}
}
res.add(new int[]{start , end});
return res.toArray(new int[res.size()][]);
}
}
代码随想录代码:
java
/**
时间复杂度 : O(NlogN) 排序需要O(NlogN)
空间复杂度 : O(logN) java 的内置排序是快速排序 需要 O(logN)空间
*/
class Solution {
public int[][] merge(int[][] intervals) {
List<int[]> res = new LinkedList<>();
//按照左边界排序
Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0]));
//initial start 是最小左边界
int start = intervals[0][0];
int rightmostRightBound = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
//如果左边界大于最大右边界
if (intervals[i][0] > rightmostRightBound) {
//加入区间 并且更新start
res.add(new int[]{start, rightmostRightBound});
start = intervals[i][0];
rightmostRightBound = intervals[i][1];
} else {
//更新最大右边界
rightmostRightBound = Math.max(rightmostRightBound, intervals[i][1]);
}
}
res.add(new int[]{start, rightmostRightBound});
return res.toArray(new int[res.size()][]);
}
}
单调递增的数字
题目描述:当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。
给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

思路:将数组转换成为字符串再转换为数组,从尾到头判断两个相邻数字的大小是否满足递增,若不满足,前一个数字减一,后一个数字变成9.
代码如下:
java
class Solution {
public int monotoneIncreasingDigits(int n) {
String str = String.valueOf(n);
char[] strArray = str.toCharArray();
// 记录需要变成9的数字开始下标
int index = strArray.length;
for(int i = strArray.length - 2; i >= 0 ; i--){
if(strArray[i] > strArray[i + 1]){
strArray[i] --;
index = i + 1;
}
}
for(int i = index ; i < strArray.length ; i++)
strArray[i] = '9';
return Integer.parseInt(String.valueOf(strArray));
}
}
另一种方法(创建了String数组,多次使用Integer.parseInt了方法,这导致不管是耗时还是空间占用都非常高,用时12ms)
java
class Solution {
public int monotoneIncreasingDigits(int N) {
String[] strings = (N + "").split("");
int start = strings.length;
for (int i = strings.length - 1; i > 0; i--) {
if (Integer.parseInt(strings[i]) < Integer.parseInt(strings[i - 1])) {
strings[i - 1] = (Integer.parseInt(strings[i - 1]) - 1) + "";
start = i;
}
}
for (int i = start; i < strings.length; i++) {
strings[i] = "9";
}
return Integer.parseInt(String.join("",strings));
}
}
监控二叉树
题目描述:给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
思路:对于二叉树的题目,使用递归解决问题的时候需要考虑是应该使用前中后哪一种顺序。在本题目之中,我们需要使用最少数量的摄像头覆盖二叉树的所有节点,那么我们就一定不能在叶子节点部署摄像头,所以我们需要隔层安放摄像头。
基于此,我们对每个节点定义三种状态:
0:该节点没有被摄像头覆盖
1:该节点安放摄像头
2:该节点被摄像头覆盖
所以,对于一个非叶子节点,他的状态会存在三种:
1.该节点的两个叶子节点被覆盖,该节点就应该先赋值为0;
2.该节点的左节点或者右节点为0,则说明该节点必须安装摄像头。
3.该节点的至少一个叶子节点安装摄像头,则该节点应该被标记为2(被覆盖)。
此外,递归结束之后,root节点的值可能还会出现为0的情况,需要单独处理,若递归函数返回值为0,则摄像头的数量应该+1;
代码如下:
java
/**
* 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 result = 0;
public int minCameraCover(TreeNode root) {
// if(root.left == null && root.right == null) return 1;
// 还会存在root节点为0的情况,因此,需要再次判断
if(minCameraCounter(root)==0) result++;
return result;
}
/**
节点的状态值:
0 表示无覆盖
1 表示 有摄像头
2 表示有覆盖
后序遍历,根据左右节点的情况,来判读 自己的状态
*/
// 1.确定递归函数的返回类型和传入参数类型
private int minCameraCounter(TreeNode node){
// 确定递归函数的结束条件
// 1.当节点为叶子节点的时候,我们应该将他的值赋值为2
if(node == null) return 2;
// left
int left = minCameraCounter(node.left);
// right
int right = minCameraCounter(node.right);
// 情况1:当节点的左右孩子的值均为2,则证明节点不用部署摄像头
if(left == 2 && right == 2){
return 0;
}
// 情况2:当节点的左孩子或右孩子的值为0的时候,就代表该节点应该部署摄像头
if(left == 0 || right == 0){
result++;
return 1;
}
// 情况3:当节点的左右孩子至少存在一个摄像头的时候,那么该节点的就应该是被覆盖了
else{
return 2;
}
}
}