零、目录
一、步骤
贪心算法一般分为如下四步:
将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解
二、题目
1、455. 分发饼干
思路1:优先考虑胃口,先喂饱大胃口
import java.util.*;
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int index = s.length - 1;
int res = 0;
for(int i = g.length - 1; i >= 0; i --) {
if(index >= 0 && s[index] >= g[i]) {
res ++;
index --;
}
}
return res;
}
}
思路2:优先考虑饼干,小饼干先喂饱小胃口
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int start = 0;
int count = 0;
for (int i = 0; i < s.length && start < g.length; i++) {
if (s[i] >= g[start]) {
start++;
count++;
}
}
return count;
}
}
2、376. 摆动序列
class Solution {
public int wiggleMaxLength(int[] nums) {
if(nums.length <= 1) {
return nums.length;
}
int cur = 0;
int pre = 0;
int res = 1;
for(int i=1; i<nums.length; i++) {
cur = nums[i] - nums[i-1];
if((cur<0 && pre>=0) || (cur>0 && pre<=0)) {
res ++;
pre = cur;
}
}
return res;
}
}
3、最大子序和
解法1:贪心
大于零比较,小于〇置〇
解法2:动态规划
dp=max(dp[i-1]+num, dp[i-1])
4、跳跃游戏
class Solution {
public boolean canJump(int[] nums) {
if(nums.length <= 1) {
return true;
}
int cover = nums[0];
for(int i = 0; i <= cover; i ++) {
cover = Math.max(cover, i + nums[i]);
if(cover >= nums.length - 1) {
return true;
}
}
return false;
}
}
1、股票无限次交易 NC134
public int maxProfit (int[] prices)
解法:逢高卖出,利润相加
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int profits = 0;
int len = prices.length;
for(int i =1;i<len;i++){
if(prices[i]>prices[i-1]){
profits+=prices[i]-prices[i-1]; //逢高卖出
}
}
return profits;
}
}
2、通配符的匹配NC44
public boolean isMatch(String s, String p)
解法1:动态规划
![](https://i-blog.csdnimg.cn/blog_migrate/d7dc89b59b5c35fd5c5021ba85843d7a.png)
dp[i][j]意思是s的前i个元素能否被p的前j个元素成功匹配。
boolean类型的dp数组,大小是[s.length+1][p.length+1],因为存在s前0个字符和p前0个字符的情况。
dp[0][0]一定是true,因为s空串和p空串是可以匹配成功的;dp[1][0]~dp[s.length][0]一定都是false,因为s不为空串而p为空串是不能匹配成功的。
dp[0][1]~dp[0][p.length]当s为空串的时候,而p不是空串的时候,当且仅当p的j字符以及前面都为'*'才为true。
dp[s.length][p.length]就得到了s和p最终的匹配情况。
然后填写dp数组剩余部分即可,状态转移方程:
当s[i]==p[j]或者p[j]=='?',则dp[i][j]=dp[i-1][j-1]。可以理解为当前字符成功匹配后,只需要考虑之前的字符串是否匹配即可;也可以理解为当前字符匹配成功之后,"移除"当前元素(即不需要再考虑当前元素)。
当p[j]==✳,则dp[i][j]=dp[i-1][j]||dp[i][j-1]。可以理解为当字符为✳的时候会出现两种情况,第一种是'*'需要作为一个字母与s[i]进行匹配;第二种是✳需要作为空字符(即不需要✳可以直接"移除"),所以dp[i][j-1];用逻辑或将两种情况连接,是因为只要有一种情况可以匹配成功则当前匹配成功,有点暴力算法的感觉。
最后当s[i]!=p[j]&&p[j]!=✳,dp[i][j]=false。这步可以省略,因为dp数组元素的默认值就是false,所以不必要进行显式的赋值为false。
public class Solution {
public boolean isMatch(String s, String p) { //分别对应主串和模式串
if(p.length() == 0) {
if(s.length() == 0) return true;
else return false;
}
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
for (int i = 1; i < dp[0].length; i ++ ) { // 处理第一行,注意多个"*"的情况
if(p.charAt(i - 1) == '*') dp[0][i] = dp[0][i - 1]; 否则为false
}
for (int i = 1; i < dp.length; i ++ ) {
for (int j = 1; j < dp[0].length; j ++ ) {
if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?') dp[i][j] = dp[i - 1][j - 1];
if(p.charAt(j - 1) == '*') dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
}
}
return dp[dp.length - 1][dp[0].length - 1];
}
}
解法2:贪心
如果i和j标记的字符正好相等或者j字符是'?'匹配成功,则"移除"i和j元素,即自增i、j。
否则如果j字符是✳(*号)依然可以匹配成功,则用istart和jstart分别标记i元素和j元素之后自增j。
否则如果istart>-1说明之前匹配过✳,因为✳可以匹配多个字符,所以这里要再次利用这个最近匹配过的✳匹配更多的字符,移动i标记istart的下一个字符,再让istart重新标记i元素同时移动j标记jstart的下一个字符。
上述三种情况都不满足,则匹配失败,返回false。
最后当s中的字符都判断完毕,则认为s为空,此时需要p为空或者p中只剩下星号的时候,才能成功匹配。
public class Solution {
public boolean isMatch(String s, String p) {
if (p==null||p.isEmpty())return s==null||s.isEmpty();
int i=0,j=0,istart=-1,jstart=-1,slen=s.length(),plen=p.length();
//判断s的所有字符是否匹配
while (i<slen){
//三种匹配成功情况以及匹配失败返回false
if (j<plen&&(s.charAt(i)==p.charAt(j)||p.charAt(j)=='?')){
i++;
j++;
}else if (j<plen&&p.charAt(j)=='*'){
istart=i;
jstart=j++;
}else if (istart>-1){
i=++istart;
j=jstart+1;
}else {
return false;
}
}
//s中的字符都判断完毕,则认为s为空,此时需要p为空或者p中只剩下星号的时候,才能成功匹配。
while (j<plen&&p.charAt(j)=='*')j++;
return j==plen;
}
}
3、分糖果问题
public int candy (int[] arr)
传入得分,计算需要的糖果
方法1:贪心+动态规划,从左向右遍历,从右向左遍历,第二次遍历时相加
public int candy (int[] arr) {
int n=arr.length;
int[]dp=new int[n];//记录每个孩子的糖果数目
//从左向右,若是右边得分高,那么就得到左边的糖果数+1,否则设为1
dp[0]=1;
for(int i=1;i<n;i++){
if(arr[i]>arr[i-1]) dp[i]=dp[i-1]+1;
else dp[i]=1;
}
//从右向左遍历,若是左边比右边得分高,那么糖果数右边+1
int sum=dp[n-1];
for(int i=n-2;i>=0;i--){
if(arr[i]>arr[i+1]&&(dp[i]<=dp[i+1])){
dp[i]=dp[i+1]+1;
}
sum+=dp[i];
}
return sum;
}
类似方法:
public int candy (int[] arr) {
int[] tmp= new int[arr.length];
Arrays.fill(tmp,1);
int count=0;
for(int i=1;i<arr.length;i++){
if(arr[i]>arr[i-1]){
tmp[i]=tmp[i-1]+1;
}
}
for(int i=arr.length-1;i>0;i--){
if(arr[i-1]>arr[i]){
tmp[i-1]=Math.max(tmp[i-1],tmp[i]+1);
}
}
for(int i:tmp)
count+=i;
return count;
}
也可对dp数组fill
4、拼接所有的字符串产生字典序最小的字符串 NC85
public String minString (String[] strs)
方法1:工具类的sort函数
public String minString (String[] strs) {
// write code here
StringBuffer sb = new StringBuffer();
Arrays.sort(strs, new Comparator<String>(){
@Override
public int compare (String s1, String s2) {
return (s1+s2).compareTo(s2+s1);
}
});
for (String s:strs) {
sb.append(s);
}
return sb.toString();
}
方法2:堆排序,最终存的是由小到大
import java.util.*;
public class Solution {
/**
*
* @param strs string字符串一维数组 the strings
* @return string字符串
*/
public String minString (String[] strs) {
// write code here
if(strs==null){
return null;
}
return stringsort(strs);
}
public class MinComparator implements Comparator<String>{
public int compare(String o1,String o2){
return (o1+o2).compareTo(o2+o1);
}
}
public String stringsort(String[] str){
PriorityQueue<String> minPriorityQueue=new PriorityQueue<>(new MinComparator());
for(int i=0;i<str.length;i++){
minPriorityQueue.add(str[i]);
}
StringBuilder ans=new StringBuilder();
for(int i=0;i<str.length;i++){
ans.append(minPriorityQueue.poll());
}
return ans.toString();
}
}
方法3:使用StringBuilder
import java.util.*;
public class Solution {
/**
*
* @param strs string字符串一维数组 the strings
* @return string字符串
*/
// 字典序最小判断函数
public static class MyComparator implements Comparator<String>{
public int compare(String a,String b){
return (a+b).compareTo(b+a);
}
}
public String minString (String[] strs) {
// write code here
// 特殊情况
if(strs==null || strs.length==0){
return "";
}
// 字符串数组排序,规则 new MyComparator()
Arrays.sort(strs,new MyComparator());
StringBuffer res=new StringBuffer();
// 拼接生成字符串
for(int i=0;i<strs.length;i++){
res.append(strs[i]);
}
return res.toString();
}
}
5、主持人调度 NC147
public int minmumNumberOfHost (int n, int[][] startEnd)
解法:优先队列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算成功举办活动需要多少名主持人
* @param n int整型 有n个活动
* @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间
* @return int整型
*/
public int minmumNumberOfHost (int n, int[][] startEnd) {
// write code here
Arrays.sort(startEnd,(int[]o1,int[]o2)->{
if(o1[0]==o2[0])return o1[1]-o2[1];
return o1[0]-o2[0];
});
PriorityQueue<Integer>que=new PriorityQueue<>();
int ans=0;
for(int i=0;i<startEnd.length;i++){
int start=startEnd[i][0];
int end=startEnd[i][1];
if(!que.isEmpty()&&que.peek()<=start){ 所有元素中小于当前元素
que.poll();
}else{
ans++;
}
que.add(end);
}
return ans;
}
}