贪心算法变种及Python模板_贪心算法几个经典例子python
itomcoil 2025-10-23 03:55 2 浏览
贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。以下是贪心算法的主要变种、对应的模板和解决的问题特点。
1. 区间调度问题
问题特点
- 需要从一组区间中选择最大数量的不重叠区间
- 通常要求最大化区间数量或最小化冲突
Python模板
def interval_scheduling(intervals):
"""
区间调度问题:选择最多的不重叠区间
:param intervals: List[List[int]] 区间列表,每个区间为[start, end]
:return: List[List[int]] 选择的不重叠区间
"""
if not intervals:
return []
# 按结束时间排序
intervals.sort(key=lambda x: x[1])
result = []
# 第一个区间总是被选择
result.append(intervals[0])
last_end = intervals[0][1]
# 遍历剩余区间
for i in range(1, len(intervals)):
start, end = intervals[i]
# 如果当前区间开始时间 >= 上一个选择区间的结束时间
if start >= last_end:
result.append(intervals[i])
last_end = end
return result
# 使用示例
intervals = [[1, 3], [2, 4], [3, 5], [6, 7]]
selected = interval_scheduling(intervals)
print(f"最多可以选择 {len(selected)} 个不重叠区间: {selected}")
2. 区间覆盖问题
问题特点
- 用最少的区间覆盖整个目标区间
- 需要选择能够覆盖目标范围的最小区间集合
Python模板
def interval_cover(target_interval, intervals):
"""
区间覆盖问题:用最少的区间覆盖目标区间
:param target_interval: List[int] 目标区间 [start, end]
:param intervals: List[List[int]] 可用区间列表
:return: List[List[int]] 选择的区间列表
"""
if not intervals:
return []
# 按开始时间排序
intervals.sort(key=lambda x: x[0])
result = []
target_start, target_end = target_interval
current_end = target_start
i = 0
n = len(intervals)
while current_end < target_end and i < n:
# 找到能覆盖当前起点且结束时间最远的区间
candidate = None
while i < n and intervals[i][0] <= current_end:
if candidate is None or intervals[i][1] > candidate[1]:
candidate = intervals[i]
i += 1
if candidate is None:
return [] # 无法覆盖
result.append(candidate)
current_end = candidate[1]
return result if current_end >= target_end else []
# 使用示例
target = [0, 10]
intervals = [[0, 3], [2, 6], [5, 8], [7, 10]]
selected = interval_cover(target, intervals)
print(f"覆盖区间 {target} 需要的最小区间数: {len(selected)}, 区间: {selected}")
3. 分配问题
问题特点
- 将资源分配给需求方,最大化满足需求的数量
- 常见的如饼干分配、任务分配等
Python模板
def assign_cookies(g, s):
"""
饼干分配问题:用饼干满足尽可能多的孩子
:param g: List[int] 孩子的胃口值
:param s: List[int] 饼干的尺寸
:return: int 最多能满足的孩子数量
"""
g.sort() # 孩子胃口排序
s.sort() # 饼干尺寸排序
child_i = cookie_j = 0
satisfied = 0
while child_i < len(g) and cookie_j < len(s):
if s[cookie_j] >= g[child_i]:
# 当前饼干可以满足当前孩子
satisfied += 1
child_i += 1
cookie_j += 1
return satisfied
# 使用示例
children = [1, 2, 3] # 孩子的胃口
cookies = [1, 1] # 饼干的尺寸
result = assign_cookies(children, cookies)
print(f"最多能满足 {result} 个孩子")
4. 跳跃游戏问题
问题特点
- 判断是否能到达终点或找到到达终点的最少步数
- 每一步选择能跳到最远的位置
Python模板
def can_jump(nums):
"""
跳跃游戏I:判断是否能到达最后一个位置
:param nums: List[int] 每个位置的最大跳跃长度
:return: bool 是否能到达终点
"""
max_reach = 0
n = len(nums)
for i in range(n):
if i > max_reach:
return False
max_reach = max(max_reach, i + nums[i])
if max_reach >= n - 1:
return True
return False
def min_jumps(nums):
"""
跳跃游戏II:到达最后一个位置的最少跳跃次数
:param nums: List[int] 每个位置的最大跳跃长度
:return: int 最少跳跃次数
"""
if len(nums) <= 1:
return 0
jumps = 0
current_end = 0
farthest = 0
for i in range(len(nums) - 1): # 不需要检查最后一个位置
farthest = max(farthest, i + nums[i])
if i == current_end:
jumps += 1
current_end = farthest
if current_end >= len(nums) - 1:
break
return jumps
# 使用示例
nums1 = [2, 3, 1, 1, 4]
print(f"是否能跳到终点: {can_jump(nums1)}")
print(f"最少跳跃次数: {min_jumps(nums1)}")
nums2 = [3, 2, 1, 0, 4]
print(f"是否能跳到终点: {can_jump(nums2)}")
5. 分数背包问题
问题特点
- 物品可以分割,选择单位价值最高的物品
- 目标是最大化总价值
Python模板
def fractional_knapsack(capacity, weights, values):
"""
分数背包问题:物品可以分割,最大化总价值
:param capacity: int 背包容量
:param weights: List[int] 物品重量
:param values: List[int] 物品价值
:return: float 最大总价值
"""
n = len(weights)
# 计算单位价值并排序
items = []
for i in range(n):
unit_value = values[i] / weights[i]
items.append((unit_value, weights[i], values[i]))
# 按单位价值降序排序
items.sort(key=lambda x: x[0], reverse=True)
total_value = 0.0
remaining_capacity = capacity
for unit_value, weight, value in items:
if remaining_capacity >= weight:
# 可以拿整个物品
total_value += value
remaining_capacity -= weight
else:
# 只能拿部分物品
total_value += unit_value * remaining_capacity
break
return total_value
# 使用示例
capacity = 50
weights = [10, 20, 30]
values = [60, 100, 120]
max_value = fractional_knapsack(capacity, weights, values)
print(f"背包能装的最大价值: {max_value}")
6. 霍夫曼编码问题
问题特点
- 构建最优前缀编码,最小化编码总长度
- 使用优先队列(最小堆)选择频率最小的两个节点
Python模板
import heapq
class Node:
def __init__(self, char, freq, left=None, right=None):
self.char = char
self.freq = freq
self.left = left
self.right = right
def __lt__(self, other):
return self.freq < other.freq
def build_huffman_tree(chars, freqs):
"""
构建霍夫曼树
:param chars: List[str] 字符列表
:param freqs: List[int] 频率列表
:return: Node 霍夫曼树的根节点
"""
# 创建最小堆
heap = []
for i in range(len(chars)):
heapq.heappush(heap, Node(chars[i], freqs[i]))
# 构建霍夫曼树
while len(heap) > 1:
# 取出频率最小的两个节点
left = heapq.heappop(heap)
right = heapq.heappop(heap)
# 创建新节点,频率为两个子节点频率之和
merged = Node(None, left.freq + right.freq, left, right)
heapq.heappush(heap, merged)
return heapq.heappop(heap) if heap else None
def generate_codes(root, current_code, codes):
"""
生成霍夫曼编码
:param root: Node 当前节点
:param current_code: str 当前编码
:param codes: dict 存储编码结果
"""
if root is None:
return
# 如果是叶子节点,存储编码
if root.char is not None:
codes[root.char] = current_code
return
# 递归处理左右子树
generate_codes(root.left, current_code + "0", codes)
generate_codes(root.right, current_code + "1", codes)
def huffman_coding(chars, freqs):
"""
霍夫曼编码主函数
:param chars: List[str] 字符列表
:param freqs: List[int] 频率列表
:return: dict 字符到编码的映射
"""
root = build_huffman_tree(chars, freqs)
codes = {}
generate_codes(root, "", codes)
return codes
# 使用示例
chars = ['a', 'b', 'c', 'd', 'e']
freqs = [5, 9, 12, 13, 16]
codes = huffman_coding(chars, freqs)
print("霍夫曼编码结果:")
for char, code in codes.items():
print(f"{char}: {code}")
7. 活动选择问题变种
问题特点
- 需要同时考虑多个约束条件
- 可能涉及权重、资源限制等
Python模板
def weighted_interval_scheduling(intervals):
"""
带权区间调度:每个区间有权重,选择权重和最大的不重叠区间
:param intervals: List[Tuple] 区间列表,每个区间为(start, end, weight)
:return: List[Tuple] 选择的区间列表
"""
if not intervals:
return []
# 按结束时间排序
intervals.sort(key=lambda x: x[1])
n = len(intervals)
# 计算每个区间之前最近的不重叠区间
prev = [-1] * n
for i in range(n):
for j in range(i - 1, -1, -1):
if intervals[j][1] <= intervals[i][0]:
prev[i] = j
break
# 动态规划计算最大权重
dp = [0] * (n + 1)
for i in range(1, n + 1):
# 不选择当前区间
dp[i] = dp[i - 1]
# 选择当前区间
if prev[i - 1] != -1:
dp[i] = max(dp[i], dp[prev[i - 1] + 1] + intervals[i - 1][2])
else:
dp[i] = max(dp[i], intervals[i - 1][2])
# 回溯找出选择的区间
result = []
i = n
while i > 0:
if dp[i] != dp[i - 1]:
result.append(intervals[i - 1])
i = prev[i - 1] + 1 if prev[i - 1] != -1 else 0
else:
i -= 1
result.reverse()
return result
# 使用示例
weighted_intervals = [(1, 3, 5), (2, 5, 6), (4, 6, 5), (6, 7, 4), (5, 8, 11), (7, 9, 2)]
selected = weighted_interval_scheduling(weighted_intervals)
total_weight = sum(interval[2] for interval in selected)
print(f"最大权重和: {total_weight}, 选择的区间: {selected}")
8. 加油站问题
问题特点
- 环形路线上的加油问题
- 需要找到能够完成环形路线的起点
Python模板
def can_complete_circuit(gas, cost):
"""
加油站问题:判断是否能完成环形路线
:param gas: List[int] 每个加油站的油量
:param cost: List[int] 到下一个加油站的耗油量
:return: int 起始加油站索引,-1表示无法完成
"""
n = len(gas)
total_gas = total_cost = 0
current_gas = 0
start_index = 0
for i in range(n):
total_gas += gas[i]
total_cost += cost[i]
current_gas += gas[i] - cost[i]
# 如果当前油量不足,从下一个加油站重新开始
if current_gas < 0:
start_index = i + 1
current_gas = 0
return start_index if total_gas >= total_cost else -1
# 使用示例
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
start = can_complete_circuit(gas, cost)
print(f"可以从加油站 {start} 开始完成环形路线" if start != -1 else "无法完成环形路线")
贪心算法通用解题思路
- 问题分析:确定问题是否具有贪心选择性质
- 贪心策略:设计每一步的最优选择策略
- 正确性证明:证明贪心策略能得到全局最优解
- 实现:编写代码实现贪心策略
贪心算法的适用条件
- 贪心选择性质:每一步的局部最优选择能导致全局最优解
- 最优子结构:问题的最优解包含子问题的最优解
- 无后效性:当前的选择不会影响后续选择的结果
这些模板覆盖了贪心算法的主要变种,在实际应用中可以根据具体问题进行调整和优化。
相关推荐
-
- Python编程实现求解高次方程_python求次幂
-
#头条创作挑战赛#编程求解一元多次方程,一般情况下对于高次方程我们只求出近似解,较少的情况可以得到精确解。这里给出两种经典的方法,一种是牛顿迭代法,它是求解方程根的有效方法,通过若干次迭代(重复执行部分代码,每次使变量的当前值被计算出的新值...
-
2025-10-23 03:58 itomcoil
- python常用得内置函数解析——sorted()函数
-
接下来我们详细解析Python中非常重要的内置函数sorted()1.函数定义sorted()函数用于对任何可迭代对象进行排序,并返回一个新的排序后的列表。语法:sorted(iterabl...
- Python入门学习教程:第 6 章 列表
-
6.1什么是列表?在Python中,列表(List)是一种用于存储多个元素的有序集合,它是最常用的数据结构之一。列表中的元素可以是不同的数据类型,如整数、字符串、浮点数,甚至可以是另一个列表。列...
- Python之函数进阶-函数加强(上)_python怎么用函数
-
一.递归函数递归是一种编程技术,其中函数调用自身以解决问题。递归函数需要有一个或多个终止条件,以防止无限递归。递归可以用于解决许多问题,例如排序、搜索、解析语法等。递归的优点是代码简洁、易于理解,并...
- Python内置函数range_python内置函数int的作用
-
range类型表示不可变的数字序列,通常用于在for循环中循环指定的次数。range(stop)range(start,stop[,step])range构造器的参数必须为整数(可以是内...
- python常用得内置函数解析——abs()函数
-
大家号这两天主要是几个常用得内置函数详解详细解析一下Python中非常常用的内置函数abs()。1.函数定义abs(x)是Python的一个内置函数,用于返回一个数的绝对值。参数:x...
- 如何在Python中获取数字的绝对值?
-
Python有两种获取数字绝对值的方法:内置abs()函数返回绝对值。math.fabs()函数还返回浮点绝对值。abs()函数获取绝对值内置abs()函数返回绝对值,要使用该函数,只需直接调用:a...
- 贪心算法变种及Python模板_贪心算法几个经典例子python
-
贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。以下是贪心算法的主要变种、对应的模板和解决的问题特点。1.区间调度问题问题特点需要从一组区间中选择最大数...
- Python倒车请注意!负步长range的10个高能用法,让代码效率翻倍
-
你是否曾遇到过需要倒着处理数据的情况?面对时间序列、日志文件或者矩阵操作,传统的遍历方式往往捉襟见肘。今天我们就来揭秘Python中那个被低估的功能——range的负步长操作,让你的代码优雅反转!一、...
- Python中while循环详解_python怎么while循环
-
Python中的`while`循环是一种基于条件判断的重复执行结构,适用于不确定循环次数但明确终止条件的场景。以下是详细解析:---###一、基本语法```pythonwhile条件表达式:循环体...
- 简单的python-核心篇-面向对象编程
-
在Python中,类本身也是对象,这被称为"元类"。这种设计让Python的面向对象编程具有极大的灵活性。classMyClass:"""一个简单的...
- 简单的python-python3中的不变的元组
-
golang中没有内置的元组类型,但是多值返回的处理结果模拟了元组的味道。因此,在golang中"元组”只是一个将多个值(可能是同类型的,也可能是不同类型的)绑定在一起的一种便利方法,通常,也...
- python中必须掌握的20个核心函数——sorted()函数
-
sorted()是Python的内置函数,用于对可迭代对象进行排序,返回一个新的排序后的列表,不修改原始对象。一、sorted()的基本用法1.1方法签名sorted(iterable,*,ke...
- 12 个 Python 高级技巧,让你的代码瞬间清晰、高效
-
在日常的编程工作中,我们常常追求代码的精简、优雅和高效。你可能已经熟练掌握了列表推导式(listcomprehensions)、f-string和枚举(enumerate)等常用技巧,但有时仍会觉...
- Python的10个进阶技巧:写出更快、更省内存、更优雅的代码
-
在Python的世界里,我们总是在追求效率和可读性的完美平衡。你不需要一个数百行的新框架来让你的代码变得优雅而快速。事实上,真正能带来巨大提升的,往往是那些看似微小、却拥有高杠杆作用的技巧。这些技巧能...
- 一周热门
- 最近发表
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)