452、用最少量的箭引爆气球
基本思想:
贪心
具体实现:
局部最优:当气球出现重叠,射,所用弓箭最少
全局最优:把所有气球射爆
1.为了让气球更好的重叠,需要对气球数组进行排序
按照起始位置进行排序,从前向后遍历数组,靠左尽可能让气球重复
2.气球重叠后,重叠气球中右边边界的最小值之前的区间需要一个弓箭
3.气球1,2需要一个弓箭,
气球3的左边界大于了第一组重叠气球的最小右边界,所以气球3需要另一支箭
4.题目中说满足 xstart ≤ x ≤ xend,则该气球会被引爆。
说明两个气球挨在一起不重叠也可以一起射爆,所以代码中 if (points[i][0] > points[i - 1][1])
不能是>=
代码:
class Solution { public int findMinArrowShots(int[][] points) { if (points.length == 0) return 0; Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0],o2[0])); int count = 1;//不为空至少需要一支箭 for (int i = 1; i < points.length; i++){ if (points[i][0] > points[i-1][1]){//后一个气球的左边和前一个气球的右边比,就可以看出是否重合,这里是不重合,需要一支箭 count++; }else{//重合的话,更新重叠气球的最小右边界,直到下一次不重合的时候才需要一支箭 points[i][1] = Math.min(points[i][1], points[i-1][1]); } } return count; } }
435、无重叠区间
基本思想:
贪心算法
具体实现:
1.按照右边界排序,就从左向右遍历
2.从左向右记录非交叉区间的个数
3.区间总数 - 非交叉区间的个数 = 要移除区间的个数
局部最优:因为按右边界排序的,从左向右遍历的话可以优先选右边界小的区间,
留给下一个区间的空间大一些,可以更容易避免交叉
全局最优:选取最多的非交叉区间
1. 区间1,2,3,4,5,6按照右边界排好序
每次找没有交叉的区间是,用右边界最小的来找,这样留给下一个区间的空间就越大,
第一条分割线就是区间1结束的位置
2.再找和区间1没有交叉的区间,选择区间4
区间5和区间1也没有交叉,但是区间4的右边界小于区间5,所以选择区间4,而且是按右边界小的顺序来遍历的
3.接下来找到区间6
4.有3个没有交叉的区间
5.总区间-没有交叉的区间
代码:
class Solution { public int eraseOverlapIntervals(int[][] intervals) { if (intervals.length < 2) return 0; Arrays.sort(intervals, new Comparator<int[]>(){ public int compare(int[] o1, int[] o2){ if (o1[1] != o2[1]){ return Integer.compare(o1[1], o2[1]); } else { return Integer.compare(o1[0], o2[0]); } } }); int count = 1; int edge = intervals[0][1]; for (int i = 1; i < intervals.length; i++){ if (edge <= intervals[i][0]){ count++; edge = intervals[i][1]; } } return intervals.length - count; } }
56、合并区间
基本思想:
贪心
具体实现:
1.按照左边界排序
2.局部最优:每次合并取最大的右边界,这样可以合并更多的区间
3.整体最优:合并所有重叠的区间
代码:
class Solution { public int[][] merge(int[][] intervals) { List<int[]> res = new LinkedList<>(); Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0])); int start = intervals[0][0]; for (int i = 1; i < intervals.length; i++){ //如果当前左边界大于上一个右边界,说明没有重合 if (intervals[i][0] > intervals[i-1][1]) { //就把靠左的这个和别人没重合的区间加入结果数组 res.add(new int[]{start, intervals[i - 1][1]}); //更新start为靠右的这个区间的左边界 start = intervals[i][0]; //这两个区间重合了 } else { //取当前区间右边界和上一个区间的右边界的最大值 intervals[i][1] = Math.max(intervals[i][1], intervals[i-1][1]); } } res.add(new int[]{start, intervals[intervals.length - 1][1]}); return res.toArray(new int[res.size()][]); } }