贪心习题小总结
不要怪笔者写的简陋,真的没有太多时间和精力来整理了。
要使用贪心算法就要证明,贪心策略可以得到全局最优解,因此它并没有动态规划应用那么广泛,但是一旦证明贪心策略的重要性往往可以用O(n)的复杂度解决问题。
一、分配问题
贪心策略:我们先对饼干和孩子排序,拿最小的饼干去满足胃口最小的孩子。
class Solution { public int findContentChildren(int[] g, int[] s) { int count = 0; Arrays.sort(g); Arrays.sort(s); int start = 0; for(int i = 0; i < g.length; i++) { for(int j = start; j < s.length; j++) { if(s[j] >= g[i]) { count++; start = j + 1; break; } } if(start >= s.length) break; } return count; } }
策略:
①先给每一个孩子分发一个糖果存在数组a与b中
②数组a从左向右遍历,只要右边的孩子比左边的孩子分数高就多发一枚糖果。
③数组b从右向左遍历,只要左边孩子比右边孩子分数高就多发一枚糖果。
④ans[i] = max{ a[i], b[i] }
class Solution { public int candy(int[] ratings) { int[] m1 = new int[ratings.length]; int[] m2 = new int[ratings.length]; Arrays.fill(m1,1); Arrays.fill(m2,1); for(int i = 1; i < ratings.length; i++) { if(ratings[i]>ratings[i-1]) m1[i] = m1[i-1] + 1; } for(int i = ratings.length-2; i >=0; i--) { if(ratings[i]>ratings[i+1]) m2[i] = m2[i+1] + 1; } int ans = 0; for(int i = 0; i < ratings.length; i++) { ans = ans + Math.max(m1[i],m2[i]); } return ans; } }
二、区间调度问题
这类问题应该是比较经典的贪心问题
class Solution { public int eraseOverlapIntervals(int[][] intervals) { if(intervals.length == 0) return 0;//这个位置一定注意空值输入,几乎必有 int count = 1; Arrays.sort(intervals,new Comparator<int[]>() { public int compare(int[] o1, int[] o2) { return o1[1] - o2[1]; } }); int x_end = intervals[0][1]; for(int i = 0; i < intervals.length; i++) { if(intervals[i][0] >= x_end) { count++; x_end = intervals[i][1]; } } return intervals.length - count; } }
class Solution { public int findMinArrowShots(int[][] points) { if(points.length == 0) return 0; int count = 0; Arrays.sort(points,new Comparator<int[]>(){ public int compare(int[] a, int[] b) { return a[1] - b[1]; } }); int s = points[0][1]; for(int i = 0; i < points.length; i++) { if(points[i][0] <= s && s <= points[i][1] ) { continue; } count++; s = points[i][1]; } return ++count; } }
策略:
①先排序,按照身高降序,k值升序排序
②按照k值插入到相应的位置,应为排序过后前面高个子肯定大于等于k,要是不大于等于k测试用例就错了
class Solution { public int[][] reconstructQueue(int[][] people) { Arrays.sort(people, new Comparator<int[]>() { public int compare(int[] o1, int[] o2) { if(o1[0] < o2[0]) return 1; else if(o1[0] == o2[0]) return o1[1]-o2[1]; else return -1; } }); LinkedList<int[]> list = new LinkedList<>(); for(int[] i : people) { list.add(i[1],i); } return list.toArray(new int[list.size()][2]); } }
三、跳跃问题
策略:从后向前遍历,能够到达终点的就是好位置,相反则为坏位置,我们始终记录最左边的好位置,只要本点能够跳过好位置,本点就是好位置
class Solution { public boolean canJump(int[] nums) { int lastgoodpos = nums.length-1; for(int i = lastgoodpos-1; i >=0; i--) { if(i+nums[i] >= lastgoodpos) lastgoodpos = i; } return lastgoodpos == 0; } }
策略:本跳应该跳到(下一跳可以跳到最远的)那个位置,这样保证用最少的跳跳到最远的距离。
相当于在求第i跳可以跳到的最远距离
class Solution { //贪心策略:本次跳跳到(下一次跳能够跳到最远的距离)的位置 //本次跳3,下一跳则能够跳到更远 public int jump(int[] nums) { int end = 0;//本次跳的最远的距离 int maxpos = 0;//下次跳可以跳的最远的距离 int ans = 0; for(int i = 0; i < nums.length-1; i++) { maxpos = Math.max(maxpos,i+nums[i]); if(i == end) { ans++; end = maxpos; } } return ans; } }
四、其他问题
应该比较简单吧,只要找好字母最后一次出现的位置就好,因为至少是从这里开始划分的
class Solution { public List<Integer> partitionLabels(String S) { HashMap<Character,Integer> hash = new HashMap<>(); LinkedList<Integer> ans = new LinkedList<>(); for(int i = 0; i < S.length(); i++) hash.put(S.charAt(i),i); int start = 0; int end = hash.get(S.charAt(0)); int max = -1; for(int i = 0; i < S.length(); i++) { int temp = hash.get(S.charAt(i)); max = Math.max(max,temp); if(i == end) { if(max>end) end = max; else { ans.add(end-start+1); start = end + 1; max = -1; if(start<S.length()) end = hash.get(S.charAt(start)); } } } return ans; } }
重点应该在边界处理上吧
public boolean canPlaceFlowers(int[] flowerbed, int n) { int count = 0; for(int i = 0; i < flowerbed.length; i++) { int l = i == 0 ? 0 : flowerbed[i-1]; int r = i == flowerbed.length-1 ? 0 : flowerbed[i+1]; if(flowerbed[i] == 0 && l == 0 && r == 0) { count++; flowerbed[i] = 1; } } return count >= n; }
感觉代码这种东西敲了和没敲真的不一样,敲了可能就会一点,不敲肯定不会。