算法与数据结构——算法基础——贪心算法(java)(b站左程云课程笔记总结)

贪心算法

在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫做贪心算法

不从整体最优上加以考虑,所做出的是在某种意义上的局部最优解

如何从局部最优推到整体最优,这个点有时候不需要去证明(证明方式难)

贪心算法在笔试时的解题套路

  1. 实现一个不依靠贪心策略的解法X,可以用暴力尝试
  2. 脑补出贪心策略A、B…
  3. 用解法X和对数器去验证每一个贪心策略,用实验的方式得知哪个贪心策略正确(或能举出反例)
  4. 不要去纠结贪心策略的证明

注:贪心算法基本只会出现在笔试(淘汰率),面试很少出现(考察不了coding能力,没有区分度)

贪心策略在实现时,经常使用到的技巧

  1. 根据某标准建立一个比较器来排序
  2. 根据某标准建立一个比较器来组成堆

会议安排问题

一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲,给你每一个项目开始的时间和结束的时间(一个数组,里面是一个个具体的项目),请你安排宣讲的日程,要求会议室进行的宣讲的次数最多,返回这个最多的宣讲次数

贪心策略:

  1. 开始时间最早先开始(不行)(如果有一个会议最早开始并且开一天)
  2. 持续时间少的先安排(不行)(中间有个时间短的同时与前后两个冲突)
  3. 会议结束的早先安排(可以)

能使这一天接下来的剩余时间最多

public class Program{
    public int start;
    public int end;
    
    public Program(int start,int end){
        this.start=start;
        this.end=end;
    }
}

public class ProgramComparator implements Comparator<Program>{
    @Override
    public int compare(Program o1,Program o2){
        return o1.end-o2.end;
    }
}

//timePoint 开始时间
public int bestArrange(Program[] programs,int timePoint){
    Arrays.sort(programs,new ProgramComparator());
    int res=0;
    for(int i=0;i<program.length;i++){
        if(timePoint<=program[i].start){
            res++;
            timePoint=programs[i].end;
        }
    }
    return res;
}

字典序最小问题

比较两个字符串的字典序,短的字符串先补0变成和长的字符串一样长,然后从第一个字符开始依次比较

image-20220616160151415

题目:给定一个字符串数组,要求将数组中所有字符串进行拼接,返回拼接后字典序最小的拼接字符串

思路一:按字典序从小到大排序并拼接(不行)

image-20220616160612857

思路二:字符串a与字符串b拼接起来的字典序是否小于等于字符串b与字符串a拼接起来的字典序(可以)

有效的比较策略+算法的有效性

注:一个有效的比较策略要具有传递性,不要形成环

image-20220616161559946

public class MyComparator implements Comparator<String>{
    @Override
    public int compare(String a,String b){
        return (a+b).compareTo(b+a);//在java中compareTo返回的是字典序的比较结果
    }
}

public String lowestString(String[] strs){
    if(strs==null||strs.length==0){
        return "";
    }
    Arrays.sort(strs,new MyComparator());
    String res="";
    for(int i=0;i<strs.length;i++){
        res+=strs[i];//拼接
    }
    return res;
}

证明过程:很难!不要碰!

image-20220616162838861

切金条问题(经典哈夫曼编码问题)

image-20220616163227166

先把所有数放到小根堆中,弹出两个数,做结合,把结合(加起来的数)放到小根堆中,再拿出两个数做结合,依次循环直到小根堆为空

public class MinheapComparator implements Comparator<Integer>{
    @Override
    public int compare(Integer o1,Integer o2){
        return o1-o2;
    }
}

public int lessMoney(int[] arr){
    PriorityQueue<Integer> pq=new PriorityQueue<>(new MinheapComparator());
    for(int i=0;i<arr.length;i++){
        pq.add(arr[i]);
    }
    int sum=0;
    int cur=0;
    while(pq.size()>1){//注意这里的条件
        cur=pq.poll()+pq.poll();
        sum+=cur;
        pq.add(cur);
    }
    return sum;
}

项目花费、利润问题

image-20220616165902240

image-20220616170326935

注:项目不能重复做

M:初始资金 k:可做的项目数

准备一个小根堆(用花费的金额来存)(锁住)和一个大根堆(利润)(解锁)

public class Node{
    public int p;
    public int c;
    public Node(int p,int c){
        this.p=p;
        this.c=c;
    }
}

public class MinCostComparator implements Comparator<Node>{
    @Override
    public int compare(Node o1,Node o2){
        return o1.c-o2.c;
    }
}

public class MaxProfitComparator implements Comparator<Node>{
    @Override
    public int compare(Node o1,Node o2){
        return o2.p-o1.p;
    }
}

public int findMaximizedCapital(int k,int w,int[] Profits,int[] Capital){
    PriorityQueue<Node> minCostQ=new PriorityQueue<>(new MinCostComparator());
    PriorityQueue<Node> maxProfitQ=new PriorityQueue<>(new MaxProfitComparator());
    for(int i=0;i<Profits.length;i++){
        minCostQ.add(new Node(Profits[i],Capital[i]));
    }
    for(int i=0;i<k;i++){//i<k
        while(!minCostQ.isEmpty()&&minCostQ.peek().c<=w){
            maxProfitQ.add(minCostQ.poll());
        }
        if(maxProfitQ.isEmpty()){
            return w;
        }
        w+=maxProfitQ.poll().p;
    }
    return w;
}

补充:一个数据流中如何做到随时可以取得中位数

与贪心无关

image-20220616172428912

准备一个大根堆一个小根堆,第一个数先放进大根堆,第二个数进来的时候

  1. 判断当前数是否小于等于大根堆的堆顶,如果是则放入大根堆,否则放入小根堆
  2. 判断大根堆和小根堆的数量,如果相差等于2,则调整,堆顶的数到数量较少的堆中

这样较小的N/2个数在大根堆堆顶,较大的N/2个数在小根堆堆顶

时间复杂度低:调整时间都是O(logN)

public static class MaxHeapComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {//比较器的另外一种写法
        if (o2 > o1) {
            return 1;
        } else {
            return -1;
        }
    }
}

public static class MinHeapComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        if (o2 < o1) {
            return 1;
        } else {
            return -1;
        }
    }
}

public class MedianHolder{
    public PriorityQueue<Integer> maxHeap=new PriorityQueue<>(new MaxHeapComparator());
    public PriorityQueue<Integer> minHeap=new PriorityQueue<>(new MinHeapComparator());
    
    public void modifyTwoHeapSize(){
        if(this.maxHeap.size()==this.minHeap.size()+2){
            this.minHeap.add(this.maxHeap.poll());
        }
        if(this.minHeap.size()==this.maxHeap.size()+2){
            this.maxHeap.add(this.minHeap.poll());
        }
    }
    
    public void addNumber(int num){
        if(maxHeap.isEmpty()||num<=maxHeap.peek()){
            maxHeap.add(num);
        }else{
            minHeap.add(num);
        }
        modifyTwoHeapSize();//z
    }
    
    public Integer getMidian(){
        int maxHeapSize=this.maxHeap.size();
        int minHeapSize=this.minHeap.size();
        if(maxHeapSize+minHeapSize==0){
            return null;
        }
        Integer maxHeapHead=this.maxHeap.peek();
        Integer minHeapHead=this.minHeap.peek();
        if(((maxHeapSize+minHeapSize)>>1)==0){
            return (maxHeapHead+minHeapHead)>>1;
        }
        return maxHeapSize>minHeapSize?maxHeapHead:minHeapHead;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值