經常使用算法之貪心算法

思路:求解問題時,老是選當前最好的選擇,不從總體上考慮。於是選用貪心算法必須保證當前選的最好的一定是總體最好的html

示例

分發餅乾

假設你是一位很棒的家長,想要給你的孩子們一些小餅乾。可是,每一個孩子最多隻能給一塊餅乾。對每一個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們知足胃口的餅乾的最小尺寸;而且每塊餅乾 j ,都有一個尺寸 sj 。若是 sj >= gi ,咱們能夠將這個餅乾 j 分配給孩子 i ,這個孩子會獲得知足。你的目標是儘量知足越多數量的孩子,並輸出這個最大數值。
假設輸入[1,2], [1,2,3],那麼輸出爲2。分析以下算法

  • 要儘量的知足更多的小孩,那麼最小尺寸的餅乾應該分給最小胃口的那我的,這樣纔不至於後面胃口大的小孩吃不到,兒胃口大的小孩吃小的確定沒法知足。這種選擇剛好也是全局最佳的選擇
public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int i=0;
        int j=0;
        int num=0;
        while(i<g.length && j<s.length){
            if(s[j]>=g[i]){
                num++;
                i++;
                j++;
            }else{
                j++;
            }
        }
        return num;
    }
複製代碼

任務調度器

給定一個用字符數組表示的 CPU 須要執行的任務列表。其中包含使用大寫的 A - Z 字母表示的26 種不一樣種類的任務。任務能夠以任意順序執行,而且每一個任務均可以在 1 個單位時間內執行完。CPU 在任何一個單位時間內均可以執行一個任務,或者在待命狀態。數組

然而,兩個相同種類的任務之間必須有長度爲 n 的冷卻時間,所以至少有連續 n 個單位時間內 CPU 在執行不一樣的任務,或者在待命狀態。bash

你須要計算完成全部任務所須要的最短期。
假如輸入 tasks = ['A','A','A','B','B','B'], n = 2 輸出爲8,執行順序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B。分析以下cookie

  • 爲了使得總體時間最短,那麼冷卻時間確定是最少的,所以要儘量保證兩個相同的任務之間的執行間隔爲n。換句話說就是貪心的選擇執行n個不同的任務,使得CPU可以充分利用
  • 要選擇先執行的任務,得考慮如何使得當前選擇總體是最優的,加入隨便選擇一個任務A執行,當存在一個任務B它的任務數比選擇的任務數要多時,這意味着B之間少了一次執行A的機會,也就是增長了要冷卻的風險,於是每次選擇任務數最多的來執行
public int leastInterval(char[] tasks, int n) { 
        int[] taskArr=new int[26];
        for(char c:tasks){
            taskArr[c-'A']++;
        }
        int maxLength=n;
        int interval=0;
        while (havaTask(taskArr)){
            //先執行任務數最多的任務
            int top = getTopTaskNotExecute(taskArr,Collections.emptyList());
            interval++;
            List<Integer> list = new ArrayList<>();
            list.add(top);
            for (int i=0;i<maxLength;i++)
            {
                //貪心的選擇沒有執行過的‘n’個任務最多並且沒有執行過的任務
                Integer nextTop = getTopTaskNotExecute(taskArr, list);
                if (nextTop==-1){
                    maxLength=list.size()-1;
                    break;
                }
                interval++;
                list.add(nextTop);
            }
            if (list.size()-1!=n && havaTask(taskArr)){
                interval+=n-list.size()+1;
            }
        }
        return interval;
    }
    
    public boolean havaTask(int[] tasks){
        for(int i=0;i<tasks.length;i++){
            if(tasks[i]>0){
                return true;
            }
        }
        return false;
    }
    
    public Integer getTopTaskNotExecute(int[] tasks,List<Integer> executeTasks){
        int maxTaskNums=0;
        int maxNumsTaskIndex=-1;
        for(int i=0;i<tasks.length;i++){
            if(!executeTasks.contains(i) && tasks[i]>maxTaskNums){
                maxTaskNums=tasks[i];
                maxNumsTaskIndex=i;
            }
        }
        if(maxNumsTaskIndex!=-1){
          tasks[maxNumsTaskIndex]--;  
        }
        return maxNumsTaskIndex;
    }
}
複製代碼

固然若是隻要解決問題,能夠直接計算出結果ui

仍是使用貪心的策略來做爲邏輯考慮的spa

  • 假設只有1個任務數最多,並且是k個,共須要至少間隔數爲 k-1 個間隔,那麼間隔消耗時間爲 n*(k-1),k個任務自己執行時間爲 k,總共時間至少爲 n*(k-1)+k
  • 假設最多的任務數同樣的不止1個有m個,當執行完一遍最多任務數一個時,還有剩餘的 m-1 個任務,並且他們的數量確定都是1,且各不相同,因此總共時間爲 n*(k-1)+k+(m-1)=(n+1)*(k-1)+m
  • 上述假設只是從單個任務的最多量來看的,沒有考慮獨立的任務數,有多少個任務確定至少要執行多少的時間,最終取最大值便可
public int leastInterval(char[] tasks, int n) { 
int[] taskArr=new int[26];
for(char c:tasks){
    taskArr[c-'A']++;
}
Arrays.sort(taskArr);
int m=0;
for(int i=25;i>-1;i--){
    if(taskArr[i]==taskArr[25]){
        m++;
    }else{
        break;
    }
}
return Math.max(tasks.length,(taskArr[25]-1)*(n+1)+m);
}
複製代碼

附錄

貪心算法思路code

相關文章
相關標籤/搜索