在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫做贪心算法
不从整体最优上加以考虑,所做出的是在某种意义上的局部最优解
如何从局部最优推到整体最优,这个点有时候不需要去证明(证明方式难)
贪心算法在笔试时的解题套路- 实现一个不依靠贪心策略的解法X,可以用暴力尝试
- 脑补出贪心策略A、B…
- 用解法X和对数器去验证每一个贪心策略,用实验的方式得知哪个贪心策略正确(或能举出反例)
- 不要去纠结贪心策略的证明
注:贪心算法基本只会出现在笔试(淘汰率),面试很少出现(考察不了coding能力,没有区分度)
贪心策略在实现时,经常使用到的技巧- 根据某标准建立一个比较器来排序
- 根据某标准建立一个比较器来组成堆
一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲,给你每一个项目开始的时间和结束的时间(一个数组,里面是一个个具体的项目),请你安排宣讲的日程,要求会议室进行的宣讲的次数最多,返回这个最多的宣讲次数
贪心策略:
- 开始时间最早先开始(不行)(如果有一个会议最早开始并且开一天)
- 持续时间少的先安排(不行)(中间有个时间短的同时与前后两个冲突)
- 会议结束的早先安排(可以)
能使这一天接下来的剩余时间最多
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{
@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
if(timePoint<=program[i].start){
res++;
timePoint=programs[i].end;
}
}
return res;
}
字典序最小问题
比较两个字符串的字典序,短的字符串先补0变成和长的字符串一样长,然后从第一个字符开始依次比较
题目:给定一个字符串数组,要求将数组中所有字符串进行拼接,返回拼接后字典序最小的拼接字符串
思路一:按字典序从小到大排序并拼接(不行)
思路二:字符串a与字符串b拼接起来的字典序是否小于等于字符串b与字符串a拼接起来的字典序(可以)
有效的比较策略+算法的有效性
注:一个有效的比较策略要具有传递性,不要形成环
public class MyComparator implements Comparator{ @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 res+=strs[i];//拼接 } return res; }
证明过程:很难!不要碰!
切金条问题(经典哈夫曼编码问题)先把所有数放到小根堆中,弹出两个数,做结合,把结合(加起来的数)放到小根堆中,再拿出两个数做结合,依次循环直到小根堆为空
public class MinheapComparator implements Comparator项目花费、利润问题{ @Override public int compare(Integer o1,Integer o2){ return o1-o2; } } public int lessMoney(int[] arr){ PriorityQueue pq=new PriorityQueue<>(); for(int i=0;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; }
注:项目不能重复做
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{
@Override
public int compare(Node o1,Node o2){
return o1.c-o2.c;
}
}
public class MaxProfitComparator implements Comparator{
@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 minCostQ=new PriorityQueue<>(new MinCostComparator());
PriorityQueue maxProfitQ=new PriorityQueue<>(new MaxProfitComparator());
for(int i=0;i
minCostQ.add(new Node(Profits[i],Capital[i]));
}
for(int i=0;i//i
maxProfitQ.add(minCostQ.poll());
}
if(maxProfitQ.isEmpty()){
return w;
}
w+=maxProfitQ.poll().p;
}
return w;
}
补充:一个数据流中如何做到随时可以取得中位数
与贪心无关
准备一个大根堆一个小根堆,第一个数先放进大根堆,第二个数进来的时候
- 判断当前数是否小于等于大根堆的堆顶,如果是则放入大根堆,否则放入小根堆
- 判断大根堆和小根堆的数量,如果相差等于2,则调整,堆顶的数到数量较少的堆中
这样较小的N/2个数在大根堆堆顶,较大的N/2个数在小根堆堆顶
时间复杂度低:调整时间都是O(logN)
public static class MaxHeapComparator implements Comparator{ @Override public int compare(Integer o1, Integer o2) {//比较器的另外一种写法 if (o2 > o1) { return 1; } else { return -1; } } } public static class MinHeapComparator implements Comparator { @Override public int compare(Integer o1, Integer o2) { if (o2 < o1) { return 1; } else { return -1; } } } public class MedianHolder{ public PriorityQueue maxHeap=new PriorityQueue<>(new MaxHeapComparator()); public PriorityQueue 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:minHeapHe; } }



