经常使用算法之贪心算法

思路:求解问题时,老是选当前最好的选择,不从总体上考虑。于是选用贪心算法必须保证当前选的最好的一定是总体最好的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

相关文章
相关标签/搜索