什么是贪心算法
贪心算法是常见的优化问题的方法,它在问题的每个阶段都寻求最优解,既贪心的做出局部最优解
零钱兑换问题
一个简单的贪心算法问题
题干:你有一个硬币数组coins[](数组中默认增序),里面存储了若干面值的硬币,写一个函数coinChangeGreedy(int[] coins,int atm),判断是否能用硬币数组里的面额付清atm(目标金额),若能返回找钱次数count,若不能则返回-1。
思考:我们要解决这个问题的步骤首先是找到数组coins[]中小于等于atm的最大金额coins[i],然后目标金额atm-coins[i],count++。
通过思考我们需要一个循环套一个循环,大循环执行atm-coins[i],count++,内部循环寻找符合满足coins[i] <= atm的第一个元素。
代码如下
int coinChangeGreedy(int[] coins, int atm) {
int i = coins.length - 1;
int count = 0;//标识找钱次数
while (atm > 0) {
//每次都找到小于等于atm的钱
while (i > 0 && coins[i] > atm) {
i--;
}
//找到最适合的coins[i]
atm -= coins[i];
count++;
}
//返回找钱次数,若找不开则返回-1
return atm == 0 ? count : -1;
}
贪心算法的优点和局限
贪心算法操作直接,实现简单,通常效率也高,但可能不会找到最优解。
在coins[1,5,10,20,,50,100]来找任意金额都能找到最优解
但coins[1,20,50]来找60只能找到一个50,十个1,一共11个硬币,但动态规划直接能找到3个20
在coins[1,49,50]来找98,用贪心算法要找一张50和四十八张1共49个,但动态规划只需要2个
贪心算法的性质
整个问题的最优解是每个局部问题的最优解
贪心算法典型用例
硬币找零问题:在某些硬币组合下,贪心算法总是可以得到最优解。
区间调度问题:假设你有一些任务,每个任务在一段时间内进行,你的目标是完成尽可能多的任务。如果每次都选择结束时间最早的任务,那么贪心算法就可以得到最优解。
分数背包问题:给定一组物品和一个载重量,你的目标是选择一组物品,使得总重量不超过载重量,且总价值最大。如果每次都选择性价比最高(价值 / 重量)的物品,那么贪心算法在一些情况下可以得到最优解。
股票买卖问题:给定一组股票的历史价格,你可以进行多次买卖,但如果你已经持有股票,那么在卖出之前不能再买,目标是获取最大利润。
霍夫曼编码:霍夫曼编码是一种用于无损数据压缩的贪心算法。通过构建霍夫曼树,每次选择出现频率最低的两个节点合并,最后得到的霍夫曼树的带权路径长度(编码长度)最小。
Dijkstra 算法:它是一种解决给定源顶点到其余各顶点的最短路径问题的贪心算法。