1. 程式人生 > 其它 >【演算法題型總結】--2、貪心演算法

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