今天做了三道题,都是需要先排序Arrays.sort()再进行贪心算法
贪心算法
实则就是通过局部最优推导出全局最优,推导回去时又找不到反例,则可以用贪心算法解决
这里涉及到一个问题
就是对二维数组进行排序。需要重写sort()方法中的Comparator比较器
可使用lambda表达式对比较器进行简写 ->
Arrays.sort(people, (a, b) -> {
if (a[0] == b[0]) return a[1] - b[1];
return b[0] - a[0];
});
含义:对于一个已定义的二位数组a进行如下规则排序,首先按照每一个对应的一维数组第一个元素进行升序排序(即a[][0]),若第一个元素相等,则按照第二个元素进行升序排序(a[][1])
若不相等,则第一个元素进行降序排序。
下面开始讲解例题
根据身高重建队列
本题有两个维度 h和k h表示该人的身高 k表示有k个身高大于或等于他的人
遇到两个维度权衡的时候, 一定要先确定一个维度,再确定另一个维度
可以先确定身高这个维度,身高一定是从大到小排(身高相同的话则k小的站前面)(则像上述方法 若第一个元素不相等则进行降序排序 若相等则第二个元素进行升序排列)
按照身高排序之后,优先按身高高的people的k来插入,后序插入节点也不会影响前面已经插入的节点,最终按照k的规则完成了队列。
局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性
全局最优:最后都做完插入操作,整个队列满足题目队列属性
值得注意的是:本题需要进行插入操作
对于java来说,此时申请一个链表LinkedList是一个很好的解决办法
然后用遍历的方法for
插入操作就是按照第二位元素的数值作为索引进行插入!!!
完整代码如下
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (a, b) -> {
if (a[0] == b[0]) return a[1] - b[1];
return b[0] - a[0];
});
LinkedList<int[]> que = new LinkedList<>();
for (int[] p : people) {
que.add(p[1],p);//p[1]代表取每个数组p对应的第二位元素的数值(个数),即要插入的下标
}
return que.toArray(new int[people.length][]);
}
}
模拟情景如下:
排序完的people: [[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
插入的过程:
插入[7,0]:[[7,0]]
插入[7,1]:[[7,0],[7,1]]
插入[6,1]:[[7,0],[6,1],[7,1]]
插入[5,0]:[[5,0],[7,0],[6,1],[7,1]]
插入[5,2]:[[5,0],[7,0],[5,2],[6,1],[7,1]]
插入[4,4]:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
用最少数量的箭引爆气球
局部最优:当气球出现重叠时,一起射,所用弓箭最少
全局最优:把所有气球射爆所用弓箭最少
注意:如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭。
此题其实与下题无重叠区间解法类似
即当第二个气球的左区间大于了第一个气球的右区间,则说明气球重叠了,这时候就需要多一只箭
若没有重叠,则第二个气球的右区间则为第一个气球和第二个气球右区间的最小值
完整代码如下
class Solution {
public int findMinArrowShots(int[][] points) {
if(points.length==0) return 0;
Arrays.sort(points,(a,b)->Integer.compare(a[0],b[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;
}
}
无重叠区间
按照右边界排序,就要从左向右遍历,因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。
按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历
思路:按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了。
局部最优:优先选右边界小的区间,所以从左向右遍历,留给下一个区间的空间大一些,从而尽量避免交叉。全局最优:选取最多的非交叉区间。
第一个元素相等 则第二个元素升序排序
若不相等 则第一个元素升序排序
所以代码是先按照按左边排序,不管右边顺序。相交的时候取最小的右边。
完整代码如下
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals,(a,b)->{
if(a[0]==a[0])return a[1]-b[1];
return a[0]-b[0];});
int count=0;
int edge=Integer.MIN_VALUE;
for(int i=0;i<intervals.length;i++){
if(edge<=intervals[i][0]){
edge=intervals[i][1];
}else{
count++;
}
}
return count;
}
}