贪心算法构建一个解决方案一次一步,总是选择现在看起来最好的选项 - 而不重新考虑过去的选择。没有回头路,没有向前看。
当两个属性成立时它起作用:
给定一组具有开始和结束时间的活动,选择不重叠活动的最大数量。
贪心策略: 按结束时间排序,始终选择最早完成的活动。
def max_activities(intervals):
intervals.sort(key=lambda x: x[1])
count, end = 0, 0
for s, e in intervals:
if s >= end:
count += 1
end = e
return count
为什么这有效?选择最早完成的活动可以为未来的活动留下最大的空间。没有其他选择可以做得更好。
通过重复合并两个最低频率符号来构建最佳的无前缀二进制代码。这是贪婪的:在每一步,合并最便宜的对。结果是一棵树,其中频繁的符号得到短代码。
霍夫曼编码用于 JPEG、MP3 和 ZIP 压缩。 David Huffman 在 1952 年还是一名学生时发明了它 - 这是一项家庭作业!
给定一个数组,其中 nums[i] 是从索引 i 开始的最大跳转长度,确定是否可以到达最后一个索引。
贪婪方法: 跟踪迄今为止可到达的最远索引。从左到右扫描 - 如果落后,则返回 false。
def can_jump(nums):
farthest = 0
for i, jump in enumerate(nums):
if i > farthest:
return False
farthest = max(farthest, i + jump)
return True
在跳跃游戏中,贪心变量”最远”代表什么?
沿着有加油站的环形路线行驶。在i站,您获得gas[i]燃料并花费cost[i]到达下一站。找到可以让您完成环路的起始站,或返回−1。
贪婪洞察力:如果总天然气≥总成本,则存在解决方案。追踪持续盈余;每当它低于零时,答案必须在该点之后开始。
在相同任务之间安排冷却时间为 n 的任务。 **贪婪:**总是选择最频繁的剩余任务。答案取决于最大频率以及有多少任务共享它。
登录 参与讨论
Tasks: A A A B B C, cooldown = 2
Schedule: A B C A B _ A
Total = 7
在任务调度程序中,为什么首先选择最频繁的任务来最小化空闲槽?如果你先选择最不频繁的,会出现什么问题?
**碎片背包:**您可以携带碎片物品。贪婪有效 - 按重量价值排序,首先采用最佳比例。
0/1 背包: 物品要么全有,要么全无。贪婪在这里失败,因为跳过一件重但有价值的物品可能比拿两件轻的物品更糟糕。这就需要动态规划。
Items: (weight=3, value=4), (weight=2, value=3), (weight=2, value=3)
Capacity: 4
Greedy (best ratio first): takes item 1 (w=3, v=4) → 1 remaining → can't fit rest → value = 4
Optimal: takes items 2+3 (w=4, v=6) → value = 6 ✗ Greedy fails!
为什么贪心方法对于 0/1 背包问题会失败?
✅ 当你可以证明贪婪选择属性时有效:
❌ 当局部最优值不能组成全局最优值时,失败:
标准技术:假设一个不使用贪婪选择的最佳解决方案。表明您可以将其选择之一交换为贪婪的选择,而不会使解决方案变得更糟。这证明贪婪至少同样好。
”交换论证”有什么用?
|方面|贪心|动态规划| |--------|--------|---------------------| |选择|每一步都有一个最佳选择 |探索所有子问题 | |重温过去? |从来没有|是 - 存储子结果 | |速度|通常更快 |取决于状态空间 | |正确性|仅当可证明时 |始终(如果表述正确)|
如有疑问,请开始贪婪 - 如果您无法证明它有效,请切换到 DP。
Dijkstra 的最短路径算法是贪婪的 - 它总是处理最近的未访问节点。它之所以有效,是因为边权重是非负的,确保贪婪选择是安全的。
给你面额为 [1, 3, 4] 的硬币,需要找 6 个硬币。贪婪方法选择 4+1+1=3 个硬币,但最佳选择是 3+3=2 个硬币。这些面值怎么打破贪婪选择属性呢?