Prim 最小生成树算法

Prim 算法是一种解决最小生成树问题(Minimum Spanning Tree)的算法。和 Kruskal 算法类似,Prim 算法的设计也是基于贪心算法(Greedy algorithm)

Prim 算法的思想很简单,一棵生成树必须连接所有的顶点,而要保持最小权重则每次选择邻接的边时要选择较小权重的边。Prim 算法看起来非常类似于单源最短路径 Dijkstra 算法,从源点出发,寻找当前的最短路径,每次比较当前可达邻接顶点中最小的一个边加入到生成树中。

例如,下面这张连通的无向图 G,包含 9 个顶点和 14 条边,所以期待的最小生成树应包含 (9 - 1) = 8 条边。

创建 mstSet 包含到所有顶点的距离,初始为 INF,源点 0 的距离为 0,{0, INF, INF, INF, INF, INF, INF, INF, INF}。

选择当前最短距离的顶点,即还是顶点 0,将 0 加入 MST,此时邻接顶点为 1 和 7。

选择当前最小距离的顶点 1,将 1 加入 MST,此时邻接顶点为 2。

选择 2 和 7 中最小距离的顶点为 7,将 7 加入 MST,此时邻接顶点为 6 和 8。

选择 2, 6, 8 中最小距离的顶点为 6,将 6 加入 MST,此时邻接顶点为 5。

重复上面步骤直到遍历完所有顶点为止,会得到如下 MST。

C# 实现 Prim 算法如下。Prim 算法可以达到 O(ElogV) 的运行时间,如果采用斐波那契堆实现,运行时间可以减少到 O(E + VlogV),如果 V 远小于 E 的话,将是对算法较大的改进。

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4
  5 namespace GraphAlgorithmTesting
  6 {
  7   class Program
  8   {
  9     static void Main(string[] args)
 10     {
 11       Graph g = new Graph(9);
 12       g.AddEdge(0, 1, 4);
 13       g.AddEdge(0, 7, 8);
 14       g.AddEdge(1, 2, 8);
 15       g.AddEdge(1, 7, 11);
 16       g.AddEdge(2, 3, 7);
 17       g.AddEdge(2, 5, 4);
 18       g.AddEdge(3, 4, 9);
 19       g.AddEdge(3, 5, 14);
 20       g.AddEdge(5, 4, 10);
 21       g.AddEdge(6, 5, 2);
 22       g.AddEdge(7, 6, 1);
 23       g.AddEdge(7, 8, 7);
 24       g.AddEdge(8, 2, 2);
 25       g.AddEdge(8, 6, 6);
 26
 27       // sorry, this is an undirect graph,
 28       // so, you know that this is not a good idea.
 29       List<Edge> edges = g.Edges
 30         .Select(e => new Edge(e.End, e.Begin, e.Weight))
 31         .ToList();
 32       foreach (var edge in edges)
 33       {
 34         g.AddEdge(edge.Begin, edge.End, edge.Weight);
 35       }
 36
 37       Console.WriteLine();
 38       Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
 39       Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
 40       Console.WriteLine();
 41
 42       List<Edge> mst = g.Prim();
 43       Console.WriteLine("MST Edges:");
 44       foreach (var edge in mst.OrderBy(e => e.Weight))
 45       {
 46         Console.WriteLine("\t{0}", edge);
 47       }
 48
 49       Console.ReadKey();
 50     }
 51
 52     class Edge
 53     {
 54       public Edge(int begin, int end, int weight)
 55       {
 56         this.Begin = begin;
 57         this.End = end;
 58         this.Weight = weight;
 59       }
 60
 61       public int Begin { get; private set; }
 62       public int End { get; private set; }
 63       public int Weight { get; private set; }
 64
 65       public override string ToString()
 66       {
 67         return string.Format(
 68           "Begin[{0}], End[{1}], Weight[{2}]",
 69           Begin, End, Weight);
 70       }
 71     }
 72
 73     class Graph
 74     {
 75       private Dictionary<int, List<Edge>> _adjacentEdges
 76         = new Dictionary<int, List<Edge>>();
 77
 78       public Graph(int vertexCount)
 79       {
 80         this.VertexCount = vertexCount;
 81       }
 82
 83       public int VertexCount { get; private set; }
 84
 85       public IEnumerable<int> Vertices { get { return _adjacentEdges.Keys; } }
 86
 87       public IEnumerable<Edge> Edges
 88       {
 89         get { return _adjacentEdges.Values.SelectMany(e => e); }
 90       }
 91
 92       public int EdgeCount { get { return this.Edges.Count(); } }
 93
 94       public void AddEdge(int begin, int end, int weight)
 95       {
 96         if (!_adjacentEdges.ContainsKey(begin))
 97         {
 98           var edges = new List<Edge>();
 99           _adjacentEdges.Add(begin, edges);
100         }
101
102         _adjacentEdges[begin].Add(new Edge(begin, end, weight));
103       }
104
105       public List<Edge> Prim()
106       {
107         // Array to store constructed MST
108         int[] parent = new int[VertexCount];
109
110         // Key values used to pick minimum weight edge in cut
111         int[] keySet = new int[VertexCount];
112
113         // To represent set of vertices not yet included in MST
114         bool[] mstSet = new bool[VertexCount];
115
116         // Initialize all keys as INFINITE
117         for (int i = 0; i < VertexCount; i++)
118         {
119           keySet[i] = int.MaxValue;
120           mstSet[i] = false;
121         }
122
123         // Always include first 1st vertex in MST.
124         // Make key 0 so that this vertex is picked as first vertex
125         keySet[0] = 0;
126         parent[0] = -1; // First node is always root of MST
127
128         // The MST will have V vertices
129         for (int i = 0; i < VertexCount - 1; i++)
130         {
131           // Pick thd minimum key vertex from the set of vertices
132           // not yet included in MST
133           int u = CalculateMinDistance(keySet, mstSet);
134
135           // Add the picked vertex to the MST Set
136           mstSet[u] = true;
137
138           // Update key value and parent index of the adjacent vertices of
139           // the picked vertex. Consider only those vertices which are not yet
140           // included in MST
141           for (int v = 0; v < VertexCount; v++)
142           {
143             // graph[u, v] is non zero only for adjacent vertices of m
144             // mstSet[v] is false for vertices not yet included in MST
145             // Update the key only if graph[u, v] is smaller than key[v]
146             if (!mstSet[v]
147               && _adjacentEdges.ContainsKey(u)
148               && _adjacentEdges[u].Exists(e => e.End == v))
149             {
150               int d = _adjacentEdges[u].Single(e => e.End == v).Weight;
151               if (d < keySet[v])
152               {
153                 keySet[v] = d;
154                 parent[v] = u;
155               }
156             }
157           }
158         }
159
160         // get all MST edges
161         List<Edge> mst = new List<Edge>();
162         for (int i = 1; i < VertexCount; i++)
163           mst.Add(_adjacentEdges[parent[i]].Single(e => e.End == i));
164
165         return mst;
166       }
167
168       private int CalculateMinDistance(int[] keySet, bool[] mstSet)
169       {
170         int minDistance = int.MaxValue;
171         int minDistanceIndex = -1;
172
173         for (int v = 0; v < VertexCount; v++)
174         {
175           if (!mstSet[v] && keySet[v] <= minDistance)
176           {
177             minDistance = keySet[v];
178             minDistanceIndex = v;
179           }
180         }
181
182         return minDistanceIndex;
183       }
184     }
185   }
186 }

输出结果如下:

参考资料

本篇文章《Prim 最小生成树算法》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。

时间: 2024-08-24 07:34:59

Prim 最小生成树算法的相关文章

剖析prim最小生成树算法

在上一篇博客中,已经用代码实现了这个算法.只是我认为从算法到代码实现,这是一个很大的过渡,不喜欢教科书式的顺理成章的过渡,虽然下面写的不知所云,虽然我很菜. 看完上篇博客中代码,想想其中一些问题, 1.如何将图的相互关系,用合适的数据结构来表示 2.如何搜索某一节点周围的权值路径 3.如何将新的节点加入到新的搜索库中. 第一个问题 对于这个图关系,用什么样的数据结构来表示,这里有两两之间的关系,能想到的树.队列.等等,好像都不能, 找到了一篇博客 http://blog.csdn.net/zsc

prim最小生成树算法题poj2485

开始想用kruskal算法自己写写runtime error #include<cstdio> #include<queue> #include<set> using namespace std; int a[2510][25100]; struct weight { int a,b; int value; bool operator < (const weight & rhs) const { return value < rhs.value; }

HDU1863 - 畅通工程 Prim最小生成树算法

HDU1863 - 畅通工程:http://acm.hdu.edu.cn/showproblem.php?pid=1863 这题我中午用并查集的方法AC了一次,下午学了Prim.换个姿势,再来一次 = =! 并查集的方法:http://blog.csdn.net/p_rogrammer/article/details/47979073 代码: #include <iostream> #include <cstdio> #include <vector> #include

最小生成树(MST)----普里姆(Prim)算法与克鲁斯卡尔(Kruskal)算法

1.概念:给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树. 2.应用:例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低.这就需要找到带权的最小生成树. 3.求最小生成树的算法 3.1 普里姆(Prim)算法 方法:从指定顶点开始将它加入集合中,然后将集合内的顶点与集合外的顶点所构成的所有边中选取权值最小的一条边作为生成树

c/c++ 用普利姆(prim)算法构造最小生成树

c/c++ 用普利姆(prim)算法构造最小生成树 最小生成树(Minimum Cost Spanning Tree)的概念: ? 假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路.这时,自然会考虑,如何在最节省经费的前提下建立这个公路网络. ? 每2个城市之间都可以设置一条公路,相应地都要付出一定的经济代价.n个城市之间,最多可以设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少? 普利姆(prim)算法的大致思路: ? 大致思想是:设图G顶点

笔试算法题(50):简介 - 广度优先 &amp; 深度优先 &amp; 最小生成树算法

广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search) BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间: 仅能非递归实现: 相比DFS较快,空间换时间: 适合广度大的图: 空间复杂度:邻接矩阵O(N^2):邻接表O(N+E): 时间复杂度:O(V+E): DFS优缺点: 无论是系统栈还是用户栈保存的节点数都只是树的深度,所以空间耗用小: 有递归和非递归实现: 由于有大量栈操作(特别是递归实现时候的系统调用),执行速度

hdu 1162 Eddy&#39;s picture(最小生成树算法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1162 Eddy's picture Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6866    Accepted Submission(s): 3469 Problem Description Eddy begins to like p

最小生成树算法汇总 (普里姆 &amp;&amp; 克鲁斯卡尔与并查集结合)

最小生成树: 今天研究了一下最小生成树,感觉最小生成树算法与最短路算法 相差不大,从Prim 与 Dijskrs算法可以看出 最小生成树即最小权重生成树,主要适用于 N个点之间 构造N-1线路,使N个点之间任意两点之间都可到达, 但是N个点之间 不构成回路,并且这N-1条线路的权重之和最小即 消耗最小. 注意:在构造最小生成树,加入新的节点时,不仅要保证权重最小,首要条件是 不能构成回路. 以图示为例,构造最小生成树 (一)普里姆   以下步骤 (二) 克鲁斯卡尔 最终的最小生成树 和 普里姆一

数据结构基础温故-5.图(中):最小生成树算法

图的“多对多”特性使得图在结构设计和算法实现上较为困难,这时就需要根据具体应用将图转换为不同的树来简化问题的求解. 一.生成树与最小生成树 1.1 生成树 对于一个无向图,含有连通图全部顶点的一个极小连通子图成为生成树(Spanning Tree).其本质就是从连通图任一顶点出发进行遍历操作所经过的边,再加上所有顶点构成的子图. 采用深度优先遍历获得的生成树称为深度优先生成树(DFS生成树),采用广度优先遍历获得的生成树称为广度优先生成树(BFS生成树).如下图所示,无向图的DFS生成树和BFS