❇️Day 34 第八章 贪心算法 part04
✴️今日任务
- 860.柠檬水找零
- 406.根据身高重建队列
- 452.用最少数量的箭引爆气球
❇️860.柠檬水找零
- 本题看上好像挺难,其实挺简单的,大家先尝试自己做一做。
- 题目链接:https://leetcode.cn/problems/lemonade-change/
- 视频讲解:https://www.bilibili.com/video/BV12x4y1j7DD
- 文章链接:https://programmercarl.com/0860.%E6%9F%A0%E6%AA%AC%E6%B0%B4%E6%89%BE%E9%9B%B6.html
自己的思路
利用switch case语句判断
自己的代码(✅通过100%)
踩坑:
- switch的case需要用break不然会一直执行剩下的case
public boolean lemonadeChange(int[] bills) {
int count5 = 0;
int count10 = 0;
for (int i = 0; i < bills.length; i++) {
switch (bills[i]){
case 5:
count5 ++;
break;
case 10:
count10 ++;
if(count5 > 0) count5--;
else return false;
break;
case 20:
if(count10 > 0 && count5 > 0){
count10 --;
count5 --;
}else if(count5 >= 3){
count5 -= 3;
}else{
return false;
}
break;
}
}
return true;
}
随想录思路
和我的基本一样只是换成if-else
随想录代码
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
five++;
} else if (bills[i] == 10) {
five--;
ten++;
} else if (bills[i] == 20) {
if (ten > 0) {
ten--;
five--;
} else {
five -= 3;
}
}
if (five < 0 || ten < 0) return false;
}
return true;
}
❇️406.根据身高重建队列
- 本题有点难度,和分发糖果类似,不要两头兼顾,处理好一边再处理另一边。
- 题目链接:https://leetcode.cn/problems/queue-reconstruction-by-height/
- 视频讲解:https://www.bilibili.com/video/BV1EA411675Y
- 文章链接:https://programmercarl.com/0406.%E6%A0%B9%E6%8D%AE%E8%BA%AB%E9%AB%98%E9%87%8D%E5%BB%BA%E9%98%9F%E5%88%97.html
自己的思路
先以ki排序然后再通过hi插入
难点:不会对二维数组进行排序
随想录思路
思路一样
-
按照k为下标重新插入队列就可以了,以图中{5,2} 为例:
-
在按照身高从大到小排序后:
- 局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性
- 全局最优:最后都做完插入操作,整个队列满足题目队列属性
- 整个插入过程如下:
排序完的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[][] reconstructQueue(int[][] people) {
// 身高从大到小排(身高相同k小的站前面)
Arrays.sort(people, (a, b) -> {
if (a[0] == b[0]) return a[1] - b[1]; // a - b 是升序排列,故在a[0] == b[0]的狀況下,會根據k值升序排列
return b[0] - a[0]; //b - a 是降序排列,在a[0] != b[0],的狀況会根据h值降序排列
});
LinkedList<int[]> que = new LinkedList<>();
for (int[] p : people) {
que.add(p[1],p); //Linkedlist.add(index, value),会將value插入到指定index里。
}
return que.toArray(new int[people.length][]);
}
}
二维数组排序
- 四个方法
- sort(T[] a):对指定数组进行升序排列。
- sort(T[] a, int fromIndex, int toIndex):对指定数组的指定范围升序排列。
- sort(T[] a, Comparator<? supre T> c): 根据指定比较器产生的顺序对指定对象数组进行排序。
- sort(T[] a, int fromIndex, int toIndex, Comparator<? supre T> c): 根据指定比较器产生的顺序对指定对象数组的指定范围进行排序。
- 二维数组按照第一维数组排序
int[][] nums=new int[][]{{1,3},{1,2},{5,1},{4,5},{3,3}};
//方法一,使用比较器
Arrays.sort(nums,new Comparator<int[]>(){
@Override
public int compare(int[] a,int[] b){
// 当第一维相等时比较第二维的
if(a[0] == b[0]){
return a[1]-b[1];
}else{
return a[0]-b[0];
}
}
});
// 方法二,使用 lambda 表达式
Arrays.sort(nums,(a,b) -> a[0] == b[0] ? a[1]-b[1] : a[0]-b[0]);
for (int[] num : nums) {
System.out.print(Arrays.toString(num));
}
// 结果 : [1, 2][1, 3][3, 3][4, 5][5, 1]
- 二维数组按照第二维数组排序
int[][] nums=new int[][]{{1,3},{1,2},{5,1},{4,5},{3,3}};
//方法一
Arrays.sort(nums,new Comparator<int[]>(){
@Override
public int compare(int[] a,int[] b){
// 当第二维相等时比较第一维的
if(a[1] == b[1]){
return a[0]-b[0];
}else{
return a[1]-b[1];
}
}
});
// 方法二,使用 lambda 表达式
Arrays.sort(nums,(a,b) -> a[1] == b[1] ? a[0]-b[0] : a[1]-b[1]);
for (int[] num : nums) {
System.out.print(Arrays.toString(num));
}
// 结果 : [5, 1][1, 2][1, 3][3, 3][4, 5]
❇️452. 用最少数量的箭引爆气球
- 本题是一道 重叠区间的题目,好好做一做,因为明天三道题目,都是 重叠区间。
- 题目链接:https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/
- 视频讲解:https://www.bilibili.com/video/BV1SA41167xe
- 文章链接:https://programmercarl.com/0452.%E7%94%A8%E6%9C%80%E5%B0%91%E6%95%B0%E9%87%8F%E7%9A%84%E7%AE%AD%E5%BC%95%E7%88%86%E6%B0%94%E7%90%83.html
自己的思路
就是一个取交集的问题
- 先对二维数组进行排序(根据第一个数字)
- 然后选定第一个范围的右区间A,看后面有几个(n)范围的左区间小于等于A,即可以一下扎破n+1个气球
- 然后再从第n+2个范围开始判断
自己的代码(✅通过84.56%)
踩坑:
用lambda表达式会溢出,排不了序,必须使用比较器
public static int findMinArrowShots(int[][] points) {
if(points.length == 1) return 1;
int count = 1;
Arrays.sort(points, new Comparator<int[]>() {
@Override
public int compare(int[] points1, int[] points2) {
//要升序排序,本来习惯写类似于 return o1.val - o2.val 来实现,这里由于样例中有出现
//[[-2147483646,-2147483645],[2147483646,2147483647]] 这样的例子,加减法会溢出,所以只能通过比较来实现
if (points1[1] > points2[1]) {
return 1;
} else if (points1[1] < points2[1]) {
return -1;
}
return 0;
}
}
);
for(int[] i : points) System.out.println(Arrays.toString(i));
int end = 0; //被取右区间的范围下标
int start = 1; //被取左区间的下标
while(start < points.length){
System.out.println("次数:"+count);
//重叠
if(points[start][0] <= points[end][1]){
System.out.println("气球{"+points[start][0]+", "+points[start][1]+"}可同时和气球{"+points[end][0]+", "+points[end][1]+"}一起被扎破");
}else{
end = start;
count ++;
}
start ++;
}
return count;
}
随想录思路
为了让气球尽可能的重叠,需要对数组进行排序。
如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭。
随想录代码
/**
* 时间复杂度 : O(NlogN) 排序需要 O(NlogN) 的复杂度
* 空间复杂度 : O(logN) java所使用的内置函数用的是快速排序需要 logN 的空间
*/
class Solution {
public int findMinArrowShots(int[][] points) {
// 根据气球直径的开始坐标从小到大排序
// 使用Integer内置比较方法,不会溢出
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
int count = 1; // points 不为空至少需要一支箭
for (int i = 1; i < points.length; i++) {
if (points[i][0] > points[i - 1][1]) { // 气球i和气球i-1不挨着,注意这里不是>=
count++; // 需要一支箭
} else { // 气球i和气球i-1挨着
points[i][1] = Math.min(points[i][1], points[i - 1][1]); // 更新重叠气球最小右边界
}
}
return count;
}
}