持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
贪心算法
在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫做贪心算法。 也就是说,不从整体最优上加以考虑,所作出的是在某种意义上的局部最优解。 局部最优 ——> 整体最优
贪心算法在笔试时的解题套路:
- 实现一个不依靠贪心策略的解法X,可以使用最暴力的尝试
- 脑补除贪心策略A、贪心策略B、贪心策略C...
- 用解法X和对数器,去验证每一个贪心策略,用实验的方式得知哪个贪心策略正确
- 不要去纠结贪心策略的证明
使用对数器的场景
- 有一个你想要测的方法a
- 实现一个绝对正确,但是复杂度不好的方法b
- 实现一个随机样本产生器
- 实现对比算法a和算法b的方法
- 比较方法a和方法b来验证方法a是否正确
- 如果有一个样本使得比对出错,分析是哪个方法出错
- 当样本数量很多时,如果比对测试仍然正确,可以确定方法a正确
贪心算法经典题目
项目收益问题
现有:正数数组costs、正数数组profits、正数k、正数m 含义如下:cost[i]表示i号项目的花费;profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润) k表示你只能串行的做最多k个项目;m表示你初始的资金 说明:你每做完一个项目,马上获得的收益,可以支持你去做下一个项目 输出:你最后获得的最大钱数
思路:利用小根堆(根据花费)存储这些项目,视为未解锁状态(不能做),根据资金m来将能够进行的项目依次从小根堆中弹出进入到大根堆(根据利润),然后从大根堆中拿出利润最高的项目进行执行。再继续根据资金m来解锁小根堆中的项目进入到大根堆,重复以上步骤,直到资金不足以做大根堆中的项目或者已经做了k个项目为止
代码实现:
//项目定义
public static class Node {
public int p;//利润
public int c;//花费
public Node(int p, int c){
this.p = p;
this.c = c;
}
}
//用于比较花费的比较器
public static class MinCostComparator implements Comparator<Node>{
@Override
public int compare(Node o1,Node o2){
return o1.c - o2.c;
}
}
//用于比较利润的比较器
public static class MaxProfitComparator implements Comparator<Node>{
@Override
public int compare(Node o1,Node o2){
return o2.p - o1.p;
}
}
//主方法
public static int findMaximizedCapital(int k,int M,int[] Profits,int[] Capital){
PriorityQueue<Node> minCostQ = new PriorityQueue(new MinCostComparator());//小根堆
PriorityQueue<Node> maxProfitQ = new PriorityQueue(new MaxCostComparator());//大根堆
//把所有项目放入小根堆中
for(int i = 0;i<Profits.length;i++){
minCostQ.add(new Node(Profits[i],Capital[i]));
}
//将能够进行的项目放入大根堆中
for(int i = 0li<k;i++){
while(!minCostQ.isEmpty() && minCost.peek().c <= M){
maxProfitQ.add(minCostQ.poll());
}
if(maxProfitQ.isEmpty()){
return M;
}
M += maxProfitQ.poll().p;
}
return M;
}
数据流中获取中位数
题目:在一个数据流中,随时可以取得中位数
思路:使用两个堆,一个大根堆,一个小根堆,将小的一半放在大根堆里,将大的一半放在小根堆里,则可以依据大根堆和小根堆的堆顶元素判断出中位数。
思路图解:
代码实现:
public static class MedianHolder {
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator());//大根堆
private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(new MinHeapComparator());//小根堆
//平衡两堆元素个数的函数
private void modifyTwoHeapsSize() {
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);
}
modifyTwoHeapsSize();
}
public Integer getMedian() {
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) / 2;
}
return maxHeapSize > minHeapSize ? maxHeapHead : minHeapHead;
}
}