Prim算法(Prim's Algorithm),由罗伯特·普里姆(Robert C. Prim)于1957年提出,是一种用于构造加权无向图的最小生成树(Minimum Spanning Tree, MST)的贪心算法。最小生成树是一个无环子图,它包含图中的所有顶点,并且所有边的权重之和最小。Prim算法的基本思想是逐步从一个顶点开始,每次选择一条与当前生成树相连且权重最小的边,将其加入到生成树中,直到生成树包含图中的所有顶点。
主要特点:
-
贪心策略:算法每次选择与当前生成树相连的、权重最小的边,确保每一步都是局部最优选择,最终得到全局最优解(即最小生成树)。
-
优先队列优化:为了高效地找到与当前生成树相连的最小权重边,通常使用优先队列(如二叉堆)存储候选边,这样每次都能直接获取当前最小权重边。
-
边的标记:在算法过程中,需要标记已加入到生成树中的边和尚未考虑的边,以便跟踪构建过程。
基本步骤:
-
初始化:
- 选择一个起始顶点,将其加入到最小生成树中。
- 将所有与起始顶点相邻的边加入优先队列,它们的权重即为当前最小权重。
- 标记所有其他顶点为未访问。
-
循环处理:
- 从优先队列中取出权重最小的边(确保这条边至少一端属于当前生成树)。
- 将这条边的未访问端点加入到最小生成树中。
- 将所有与新加入顶点相邻且未被标记为已加入生成树的边,按照其权重加入优先队列。
- 标记新加入顶点为已访问。
-
结束条件:当优先队列为空或所有顶点都被访问过(即已加入生成树)时,算法结束。
应用实例:
Prim算法在实际中有多种应用,包括但不限于:
- 网络设计:构建成本最低的通信网络,确保所有站点都能联通。
- 旅行规划:在多个城市间规划一条总费用最低的旅行路线,要求经过所有城市。
- 数据聚类:在相似性矩阵(视为加权无向图)中寻找一组核心顶点,使得所有顶点都与至少一个核心顶点相连,且所有边的总权重最小。
以下是Prim算法的Python实现示例(以邻接矩阵表示加权无向图,并使用内置的heapq
模块实现优先队列):
Python
1import heapq
2
3def prim(graph, start):
4 n = len(graph)
5 mst = []
6 visited = [False] * n
7 distances = {node: float('inf') for node in range(n)}
8 distances[start] = 0
9
10 priority_queue = [(0, start)] # (distance, node)
11
12 while priority_queue:
13 current_distance, current_node = heapq.heappop(priority_queue)
14
15 if visited[current_node]:
16 continue
17
18 visited[current_node] = True
19 mst.append(current_node)
20
21 for neighbor, weight in enumerate(graph[current_node]):
22 if not visited[neighbor] and weight < distances[neighbor]:
23 distances[neighbor] = weight
24 heapq.heappush(priority_queue, (weight, neighbor))
25
26 return mst
27
28# 示例
29graph = [
30 [0, 2, 0, 6, 0],
31 [2, 0, 3, 8, 5],
32 [0, 3, 0, 0, 7],
33 [6, 8, 0, 0, 9],
34 [0, 5, 7, 9, 0]
35]
36
37mst = prim(graph, 0)
38print("Minimum spanning tree nodes:", mst)
实现了Prim算法的核心逻辑,使用列表visited
记录已访问顶点,优先队列priority_queue
按权重排序待加入边,列表mst
存储最小生成树的顶点。遍历结束后,返回一个列表,其中包含了最小生成树的顶点顺序。注意,这里仅输出了最小生成树包含的顶点,若需要输出具体的边信息,可以在算法中额外记录边的添加情况。