贪心算法是一种在每一步选择中都采取当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的算法策略。它适用于一些特定的问题,比如背包问题、 Huffman 编码等。本文将精选一些贪心算法的习题,并详细解析解题技巧。
习题一:最小生成树问题
问题描述:给定一个无向图,图中的边都有一个权重,要求找出一个边的子集,使得这个子集构成一棵树,并且所有边的权重之和最小。
解题思路:使用 Kruskal 算法,按照边的权重从小到大排序,每次选择权重最小的边,如果这条边不会与已选择的边构成环,则选择这条边。
代码示例:
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = []
def add_edge(self, u, v, w):
self.graph.append([u, v, w])
def find(self, parent, i):
if parent[i] == i:
return i
return self.find(parent, parent[i])
def union(self, parent, rank, x, y):
rootx = self.find(parent, x)
rooty = self.find(parent, y)
if rank[rootx] < rank[rooty]:
parent[rootx] = rooty
elif rank[rootx] > rank[rooty]:
parent[rooty] = rootx
else:
parent[rooty] = rootx
rank[rootx] += 1
def kruskal_mst(self):
result = []
i, e = 0, 0
self.graph = sorted(self.graph, key=lambda item: item[2])
parent = []
rank = []
for node in range(self.V):
parent.append(node)
rank.append(0)
while e < self.V - 1:
u, v, w = self.graph[i]
i = i + 1
x = self.find(parent, u)
y = self.find(parent, v)
if x != y:
e = e + 1
result.append([u, v, w])
self.union(parent, rank, x, y)
return result
# 创建图
g = Graph(4)
g.add_edge(0, 1, 10)
g.add_edge(0, 2, 6)
g.add_edge(0, 3, 5)
g.add_edge(1, 3, 15)
g.add_edge(2, 3, 4)
# 打印最小生成树
print("Edge \tWeight")
for u, v, w in g.kruskal_mst():
print("%d -- %d == %d" % (u, v, w))
习题二:背包问题
问题描述:给定一个背包容量和若干物品,每个物品都有一个价值,要求选择物品放入背包,使得背包内物品的总价值最大,且不超过背包容量。
解题思路:使用动态规划解决背包问题。定义一个二维数组 dp[i][j],表示前 i 个物品放入容量为 j 的背包的最大价值。
代码示例:
def knapsack(W, wt, val, n):
dp = [[0 for x in range(W + 1)] for x in range(n + 1)]
for i in range(n + 1):
for w in range(W + 1):
if i == 0 or w == 0:
dp[i][w] = 0
elif wt[i - 1] <= w:
dp[i][w] = max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w])
else:
dp[i][w] = dp[i - 1][w]
return dp[n][W]
# 物品价值
val = [60, 100, 120]
# 物品重量
wt = [10, 20, 30]
# 背包容量
W = 50
# 物品数量
n = len(val)
# 打印背包问题的最大价值
print("Maximum value in knapsack =", knapsack(W, wt, val, n))
习题三:Huffman 编码
问题描述:给定一个字符集合及其出现频率,构造一个最优的前缀编码,使得编码后的字符串长度最短。
解题思路:使用 Huffman 树算法,将字符按照频率排序,然后构建一棵二叉树,频率高的字符在树的左侧,频率低的字符在树的右侧。
代码示例:
import heapq
class Node:
def __init__(self, char, freq):
self.char = char
self.freq = freq
self.left = None
self.right = None
def __lt__(self, other):
return self.freq < other.freq
def huffman_encoding(char_freq):
heap = [Node(char, freq) for char, freq in char_freq.items()]
heapq.heapify(heap)
while len(heap) > 1:
left = heapq.heappop(heap)
right = heapq.heappop(heap)
merged = Node(None, left.freq + right.freq)
merged.left = left
merged.right = right
heapq.heappush(heap, merged)
root = heapq.heappop(heap)
return generate_codes(root, "")
def generate_codes(node, current_code):
if node is None:
return {}
if node.char is not None:
return {node.char: current_code}
codes = {}
codes.update(generate_codes(node.left, current_code + "0"))
codes.update(generate_codes(node.right, current_code + "1"))
return codes
# 字符及其频率
char_freq = {'a': 5, 'b': 9, 'c': 12, 'd': 13, 'e': 16, 'f': 45}
# 打印 Huffman 编码
print("Huffman Codes:", huffman_encoding(char_freq))
总结
本文精选了三个贪心算法的习题,并详细解析了解题思路和代码实现。通过这些习题,可以帮助读者更好地理解和掌握贪心算法的原理和应用。在实际应用中,贪心算法可以有效地解决一些优化问题,但需要注意其局限性,并非所有问题都适用于贪心算法。
