Day28 力扣贪心 : 860.柠檬水找零| 406.根据身高重建队列 |452. 用最少数量的箭引爆气球
860.柠檬水找零
本题看上好像挺难,其实挺简单的,大家先尝试自己做一做。
https://programmercarl.com/0860.%E6%9F%A0%E6%AA%AC%E6%B0%B4%E6%89%BE%E9%9B%B6.html
第一印象
我想用哈希来存储,并同时记录一共多少钱,如果钱不够直接false。
钱够的话比如有2个10,要找15块钱,再去具体实现。
用一堆if else确实做出来了,但我觉得一点也不贪心。看看题解。
看完题解的思路
啊 原来找零20的时候优先用10+5的方式,然后再是5+5+5的方式就是贪心啊。
实现上的困难
没困难
感悟
贪心真是有时候让人……意想不到
代码随想录里说
因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!
所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。
全局最优:完成全部账单的找零。
局部最优可以推出全局最优,并找不出反例,那么就试试贪心算法!
代码
class Solution {
public boolean lemonadeChange(int[] bills) {
int total = 0;
int[] money = new int[2];
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
total += 5;
money[0]++;
}
if (bills[i] == 10) {
total += 10;
if (money[0] < 1) {
return false;
} else {
money[0]--;
money[1]++;
}
}
if (bills[i] == 20) {
total += 20;
if (money[0] >= 1 && money[1] >= 1) {
money[0]--;
money[1]--;
} else if (money[0] >= 3) {
money[0] -= 3;
} else {
return false;
}
}
}
return true;
}
}
406.根据身高重建队列
本题有点难度,和分发糖果类似,不要两头兼顾,处理好一边再处理另一边。
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
第一印象
哎卧槽题干就看不懂,看题解了。
看完题解的思路
这个题思路有点太难了。
[h, k] 也是两个维度。不能同时考虑两个,要先考虑一个再考虑一个。
先按身高排序,同身高的话,k增序排列。
之后再按 k 去调整位置。
**局部最优:**优先按身高高的people的k来插入。插入操作过后的people满足队列属性
**全局最优:**最后都做完插入操作,整个队列满足题目队列属性
实现中的困难
思路学会了我也写不出这个代码,草了。
chatgpt现在打不开,代码的实现回头再说。
感悟
代码随想录里的感悟:
关于出现两个维度一起考虑的情况,我们已经做过两道题目了,另一道就是135. 分发糖果 (opens new window)。
其技巧都是确定一边然后贪心另一边,两边一起考虑,就会顾此失彼。
这道题目可以说比135. 分发糖果 (opens new window)难不少,其贪心的策略也是比较巧妙。
最后我给出了两个版本的代码,可以明显看是使用C++中的list(底层链表实现)比vector(数组)效率高得多。
对使用某一种语言容器的使用,特性的选择都会不同程度上影响效率。
所以很多人都说写算法题用什么语言都可以,主要体现在算法思维上,其实我是同意的但也不同意。
对于看别人题解的同学,题解用什么语言其实影响不大,只要题解把所使用语言特性优化的点讲出来,大家都可以看懂,并使用自己语言的时候注意一下。
对于写题解的同学,刷题用什么语言影响就非常大,如果自己语言没有学好而强调算法和编程语言没关系,其实是会误伤别人的。
这也是我为什么统一使用C++写题解的原因
代码
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][]);
}
}
452. 用最少数量的箭引爆气球
本题是一道 重叠区间的题目,好好做一做,因为明天三道题目,都是 重叠区间。
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
第一印象
感觉就是找交集呢 直接看下题解
看完题解的思路
思路就是重复的气球尽量用一个弓箭射。
为了让气球挨着重叠,就要先排序。
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
gpt还没复活,这是一个升序。我不懂lambda表达式里用什么判断升序降序,return的正负吗????
难在怎么判断气球重复,以及我和上一个重复了,下一个和我们俩都重复吗?
- 如果我的左边界 小于等于 上一个的右边界,我俩就算重叠了
- 我和上一个最小的右边界,作为我俩的右边界 right,用于判断下一个和我是否重叠的时候,它的左边界如果和我俩最小的right都重叠,那它和我俩就重叠了。
比如1-4, 2-7, 5-6. 按照我的逻辑1-4 和2-7重叠,箭数量不变,但这个时候4就作为他俩新的右边界限了,和5-6是不重叠的。
实现时的困难
没有啥困难,就是Lambda表达式看不懂。
代码
class Solution {
public int findMinArrowShots(int[][] points) {
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
int result = 1;
for (int i = 1; i < points.length; i++) {
//我的左边界超过 上一个的右边了
if (points[i][0] > points[i - 1][1]) {
result++;
} else {
//我和上一个是重复的,不需要多一个箭射我
//更新我的右边界吧,用于判断下一个的时候,看能不能一个箭射我们仨
points[i][1] = Math.min(points[i - 1][1], points[i][1]);
}
}
return result;
}
}