【算法题型总结】--2、贪心算法
零、目录
一、步骤
贪心算法一般分为如下四步:
将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解
二、题目
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:动态规划
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; } }
本文来自博客园,作者:哥们要飞,转载请注明原文链接:https://www.cnblogs.com/liujinhui/p/15124475.html