视频讲解:
贪心算法,看上去复杂,其实逻辑都是固定的!LeetCode:860.柠檬水找零_哔哩哔哩_bilibili
贪心算法,不要两边一起贪,会顾此失彼 | LeetCode:406.根据身高重建队列_哔哩哔哩_bilibili
贪心算法,判断重叠区间问题 | LeetCode:452.用最少数量的箭引爆气球_哔哩哔哩_bilibili
860.柠檬水找零
思路:没有特别明确的贪心思想,可能涉及到贪心的思想就是在处理找零15元时,优先消耗10元的纸币,没有10元的,但消耗3张5元的。本题的关键就是统计5元,10元纸币的数量,虽然是有必要统计一下总共的利润来评估是否还有钱来找零,但是所有的找零都是花费的5元和10元,所以20元的无需统计,那么总利润统计也就没有意义,只要剩余的5元和10元纸币的数量可以应付所有找零,那么本题就可以返回true;反之返回一个false。
// 时间复杂度O(n)
// 空间复杂度O(1)class Solution {public boolean lemonadeChange(int[] bills) {// 思路和昨天的加油站的第二种解法非常的类似// 分别用于计数5元,10元,20元int five = 0;int ten = 0;for(int i=0; i<bills.length; i++){// 首先收取顾客的钱if(bills[i] == 5)five++;else if(bills[i] == 10)ten++;// 计算当前的找零int change = bills[i] - 5;if(change == 0)continue;else if(change==5 && five>0)five--;else if(change==15 && ten>0 && five>0){// 找零15的时候应当优先消耗10元,这就是贪心的一部分five--;ten--;}else if(change == 15 && five>=3)five -= 3;elsereturn false;}return true;}
}
406. 根据身高重建队列
思路:参考 代码随想录 的思路解出。明确涉及两个维度需要控制的题目时,优先确定一个维度然后再去操作另一个维度,即优先将一个维度的关系确定,然后再此基础上再去确定另一个维度。与此相同思路的题目中分发糖果,参考的左右两个相邻的位置,因此也是两个维度。
// 时间复杂度O(nlogn),是Arrays.sort()的时间复杂度
// 空间复杂度O(n)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];});List<int[]> que = new ArrayList<>();// 现在完成排序的people数组是h按照从大到小,k在h相同时才会使用到作为从小到大,用来维护之后进行赋值时的索引正确性// 而可以直接插入的缘故就是,将一个小的数加入到他的前面去不影响之前的数的h和k,因为之前的数的h都比当前数的h来的大for(int[] p:people)que.add(p[1], p);return que.toArray(new int[people.length][]);}
}
452. 用最少数量的箭引爆气球
思路:新的一种类型的题目——“重叠区间”。首先自己实现了这道题的题解,认为重叠区间类型题目的解题就是合并存在交集的区间,然后统计最终还剩下几个相互独立的区间;而贪心的策略就是如果可以时间效率最小的情况下完成区间的合并。在这个思路和两个维度的确定一个维度比较像,优先确定所有区间的下限的关系,然后再判断区间彼此之间是否存在交集,存在交集即融合即可。
// 时间复杂度O(nlogn),排序耗时
// 空间复杂度O(n)class Solution {public int findMinArrowShots(int[][] points) {// 尝试先做,思路是计算重合的区间个数if(points.length == 1)return 1;// 按照起始的位置从小到大排列,注意这里需要自己写大小判断,然后返回负数还是正数,因为采用a[0]-b[0]的方式会存在数值越界的情况Arrays.sort(points, (a,b)->{if(a[0] < b[0]) return -1;if(a[0] == b[0]) return 0;return 1;});List<int[]> scopes = new ArrayList<>();int[] cur = points[0];scopes.add(points[0]);// cur保存当前区间下限最小的,开始进行区间的合并for(int i=1; i<points.length; i++){if( (long)points[i][0] > (long)cur[1] ){scopes.add(points[i]);cur = points[i];}else{cur[0] = Math.max(cur[0], points[i][0]);cur[1] = Math.min(cur[1], points[i][1]);}}return scopes.size();}
}