目录
贪心算法(Greedy Algorithm)是一种基于贪心策略的算法设计方法,它在每一步选择中都采取当前状态下最优的选择,以期望达到全局最优解。贪心算法通常适用于那些具有最优子结构性质的问题,即问题的最优解可以通过子问题的最优解来构造。
案例四:最小生成树
给定一个带权重的连通无向图,我们希望找到一个包含所有节点的子图,使得这个子图是一棵树(没有环路)并且边的权重之和最小。这个子图被称为最小生成树。
方法:Kruskal算法是解决最小生成树问题的一种经典算法。它基于贪心策略,通过选择当前具有最小权重的边来构建最小生成树。
算法步骤:
-
初始化:将图中的所有边按照权重从小到大进行排序。
-
创建并查集:为每个节点创建一个单独的集合。
-
遍历边:按照排序后的顺序,依次考虑每一条边。
-
检查边的两个节点是否属于同一集合:
- 如果是,则忽略这条边,因为它会形成环路。
- 如果不是,则将这条边加入最小生成树中,并将边的两个节点合并到同一个集合中。
-
重复步骤3和4,直到所有节点都属于同一个集合,或者所有边都被考虑过。
-
最终结果:最小生成树就是构建过程中选择的边的集合。
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
root_x, root_y = self.find(x), self.find(y)
if root_x == root_y:
return False
if self.rank[root_x] > self.rank[root_y]:
self.parent[root_y] = root_x
elif self.rank[root_x] < self.rank[root_y]:
self.parent[root_x] = root_y
else:
self.parent[root_y] = root_x
self.rank[root_x] += 1
return True
def kruskal(graph):
edges = []
for node in graph:
for neighbor, weight in graph[node].items():
edges.append((node, neighbor, weight))
edges.sort(key=lambda x: x[2])
n = len(graph)
uf = UnionFind(n)
min_spanning_tree = []
for edge in edges:
u, v, w = edge
if uf.union(u, v):
min_spanning_tree.append((u, v, w))
return min_spanning_tree
# 示例图的邻接表表示
graph = {
'A': {'B': 4, 'C': 2},
'B': {'A': 4, 'D': 5},
'C': {'A': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
# 使用Kruskal算法找到最小生成树
min_spanning_tree = kruskal(graph)
# 输出最小生成树的边
for edge in min_spanning_tree:
print(f'Edge: {edge[0]} - {edge[1]}, Weight: {edge[2]}')
在这段代码中,我们首先定义了一个UnionFind
类,用于实现并查集。然后定义了一个kruskal
函数,它接受一个带权无向图(以邻接表的形式表示)作为输入,并返回构建出的最小生成树的边。
这个例子的输出将是:
Edge: C - D, Weight: 1
Edge: A - C, Weight: 2
Edge: A - B, Weight: 4
案例五:最优并归
最优并归问题是指将多个有序序列合并成一个有序序列,并使得合并过程中的比较次数最少。这个问题在排序算法中经常出现。
在最优并归问题中,一个常用的贪心策略是使用优先队列(通常是最小堆)来实现。
具体步骤如下:
-
初始化:将每个有序序列的第一个元素放入优先队列,同时记录每个元素所属的序列。
-
循环:从优先队列中取出最小的元素,将其加入结果序列,同时取出该元素所属序列的下一个元素放入优先队列。
-
重复:重复上述步骤,直到所有序列中的元素都被处理完毕。
假设我们有两个有序序列:
序列1:[2, 4, 6, 8, 10]
序列2:[1, 3, 5, 7, 9]
import heapq
def merge_sorted_lists(lists):
min_heap = [(lst[0], i, 0) for i, lst in enumerate(lists) if lst]
heapq.heapify(min_heap)
result = []
while min_heap:
val, lst_index, elem_index = heapq.heappop(min_heap)
result.append(val)
if elem_index + 1 < len(lists[lst_index]):
next_val = lists[lst_index][elem_index + 1]
heapq.heappush(min_heap, (next_val, lst_index, elem_index + 1))
return result
# 示例
lists = [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
merged_list = merge_sorted_lists(lists)
print("合并后的有序序列:", merged_list)
在这段代码中,我们定义了一个名为merge_sorted_lists
的函数,它接受一个包含多个有序序列的列表作为输入,并返回合并后的有序序列。
函数使用了一个最小堆(优先队列)来实现贪心策略。首先,将每个序列的第一个元素和所属序列的索引放入最小堆中。然后,在循环中,从最小堆中取出最小的元素,将其加入结果序列,并将所属序列的下一个元素放入最小堆。