【演算法題型總結】--2、貪心演算法
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; } }