不要怪笔者写的简陋,真的没有太多时间和精力来整理了。
要使用贪心算法就要证明,贪心策略可以得到全局最优解,因此它并没有动态规划应用那么广泛,但是一旦证明贪心策略的重要性往往可以用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;
}
感觉代码这种东西敲了和没敲真的不一样,敲了可能就会一点,不敲肯定不会。