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 的话,将是对算法较大的改进。
using System;
using System.Collections.Generic;
using System.Linq; namespace GraphAlgorithmTesting
{
class Program
{
static void Main(string[] args)
{
Graph g = new Graph();
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , );
g.AddEdge(, , ); // sorry, this is an undirect graph,
// so, you know that this is not a good idea.
List<Edge> edges = g.Edges
.Select(e => new Edge(e.End, e.Begin, e.Weight))
.ToList();
foreach (var edge in edges)
{
g.AddEdge(edge.Begin, edge.End, edge.Weight);
} Console.WriteLine();
Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
Console.WriteLine(); List<Edge> mst = g.Prim();
Console.WriteLine("MST Edges:");
foreach (var edge in mst.OrderBy(e => e.Weight))
{
Console.WriteLine("\t{0}", edge);
} Console.ReadKey();
} class Edge
{
public Edge(int begin, int end, int weight)
{
this.Begin = begin;
this.End = end;
this.Weight = weight;
} public int Begin { get; private set; }
public int End { get; private set; }
public int Weight { get; private set; } public override string ToString()
{
return string.Format(
"Begin[{0}], End[{1}], Weight[{2}]",
Begin, End, Weight);
}
} class Graph
{
private Dictionary<int, List<Edge>> _adjacentEdges
= new Dictionary<int, List<Edge>>(); public Graph(int vertexCount)
{
this.VertexCount = vertexCount;
} public int VertexCount { get; private set; } public IEnumerable<int> Vertices { get { return _adjacentEdges.Keys; } } public IEnumerable<Edge> Edges
{
get { return _adjacentEdges.Values.SelectMany(e => e); }
} public int EdgeCount { get { return this.Edges.Count(); } } public void AddEdge(int begin, int end, int weight)
{
if (!_adjacentEdges.ContainsKey(begin))
{
var edges = new List<Edge>();
_adjacentEdges.Add(begin, edges);
} _adjacentEdges[begin].Add(new Edge(begin, end, weight));
} public List<Edge> Prim()
{
// Array to store constructed MST
int[] parent = new int[VertexCount]; // Key values used to pick minimum weight edge in cut
int[] keySet = new int[VertexCount]; // To represent set of vertices not yet included in MST
bool[] mstSet = new bool[VertexCount]; // Initialize all keys as INFINITE
for (int i = ; i < VertexCount; i++)
{
keySet[i] = int.MaxValue;
mstSet[i] = false;
} // Always include first 1st vertex in MST.
// Make key 0 so that this vertex is picked as first vertex
keySet[] = ;
parent[] = -; // First node is always root of MST // The MST will have V vertices
for (int i = ; i < VertexCount - ; i++)
{
// Pick thd minimum key vertex from the set of vertices
// not yet included in MST
int u = CalculateMinDistance(keySet, mstSet); // Add the picked vertex to the MST Set
mstSet[u] = true; // Update key value and parent index of the adjacent vertices of
// the picked vertex. Consider only those vertices which are not yet
// included in MST
for (int v = ; v < VertexCount; v++)
{
// graph[u, v] is non zero only for adjacent vertices of m
// mstSet[v] is false for vertices not yet included in MST
// Update the key only if graph[u, v] is smaller than key[v]
if (!mstSet[v]
&& _adjacentEdges.ContainsKey(u)
&& _adjacentEdges[u].Exists(e => e.End == v))
{
int d = _adjacentEdges[u].Single(e => e.End == v).Weight;
if (d < keySet[v])
{
keySet[v] = d;
parent[v] = u;
}
}
}
} // get all MST edges
List<Edge> mst = new List<Edge>();
for (int i = ; i < VertexCount; i++)
mst.Add(_adjacentEdges[parent[i]].Single(e => e.End == i)); return mst;
} private int CalculateMinDistance(int[] keySet, bool[] mstSet)
{
int minDistance = int.MaxValue;
int minDistanceIndex = -; for (int v = ; v < VertexCount; v++)
{
if (!mstSet[v] && keySet[v] <= minDistance)
{
minDistance = keySet[v];
minDistanceIndex = v;
}
} return minDistanceIndex;
}
}
}
}
输出结果如下:

参考资料
- Connectivity in a directed graph
- Strongly Connected Components
- Tarjan's Algorithm to find Strongly Connected Components
本篇文章《Prim 最小生成树算法》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。
Prim 最小生成树算法的更多相关文章
- prim最小生成树算法(堆优化)
prim算法原理和dijkstra算法差不多,依然不能处理负边 1 #include<bits/stdc++.h> 2 using namespace std; 3 struct edge ...
- 最小生成树算法(Prim,Kruskal)
边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...
- c/c++ 用普利姆(prim)算法构造最小生成树
c/c++ 用普利姆(prim)算法构造最小生成树 最小生成树(Minimum Cost Spanning Tree)的概念: 假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路.这时 ...
- [数据结构]最小生成树算法Prim和Kruskal算法
最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总 ...
- 最小生成树算法 prim kruskal两种算法实现 HDU-1863 畅通工程
最小生成树 通俗解释:一个连通图,可将这个连通图删减任意条边,仍然保持连通图的状态并且所有边权值加起来的总和使其达到最小.这就是最小生成树 可以参考下图,便于理解 原来的图: 最小生成树(蓝色线): ...
- 最小生成树算法总结(Kruskal,Prim)
今天复习最小生成树算法. 最小生成树指的是在一个图中选择n-1条边将所有n个顶点连起来,且n-1条边的权值之和最小.形象一点说就是找出一条路线遍历完所有点,不能形成回路且总路程最短. Kurskal算 ...
- 笔试算法题(50):简介 - 广度优先 & 深度优先 & 最小生成树算法
广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search) BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间: 仅能 ...
- Kruskal 最小生成树算法
对于一个给定的连通的无向图 G = (V, E),希望找到一个无回路的子集 T,T 是 E 的子集,它连接了所有的顶点,且其权值之和为最小. 因为 T 无回路且连接所有的顶点,所以它必然是一棵树,称为 ...
- dijkstra(最短路)和Prim(最小生成树)下的堆优化
dijkstra(最短路)和Prim(最小生成树)下的堆优化 最小堆: down(i)[向下调整]:从第k层的点i开始向下操作,第k层的点与第k+1层的点(如果有)进行值大小的判断,如果父节点的值大于 ...
随机推荐
- Delphi容器类之---TOrderedList、TStack、TQueue、TObjectStack、TObjectQueue
TOrderedList.TStack.TQueue Contnrs单元还定义了其他三个类:TOrderedList.TStack.TQueue TOrderedList TOrderedList = ...
- 【Java EE 学习 72 上】【数据采集系统第四天】【增加调查logo】【文件上传】【动态错误页指定】【上传限制】【国际化】
增加logo的技术点:文件上传,国际化 文件上传的功能在struts2中是使用文件上传拦截器完成的. 1.首先需要在页面上添加一个文件上传的超链接. 点击该超链接能够跳转到文件上传页面.我给该表单页面 ...
- dbca建库sys用户被锁
奇怪问题:dbca建库sys用户被锁, 点击密码管理报账户被锁 而且在服务器上无法进行操作系统验证登陆,经过一番检查发现oracle用户和grid用户没有在dba组里 解决: 1.把oracle用户和 ...
- 多个supplemental logging的说明与删除
有时候我们做ogg的时候,查看info trandata schema.table 无法查看到对应的supplement log信息, 但是我们查看table script的时候可以看到supplem ...
- 第三十四篇:在SOUI中使用异步通知
概述 异步通知是客户端开发中常见的需求,比如在一个网络处理线程中要通知UI线程更新等等. 通常在Windows编程中,为了方便,我们一般会向UI线程的窗口句柄Post/Send一个窗口消息从而达到将非 ...
- Python-Hello world!
一.Python安装 Windows 1.下载安装包 https://www.python.org/downloads/ 2.安装 默认安装路径:C:\python3.5 3.配置环境变量: [右键计 ...
- 听大神说:https和http有何区别?(转)
在做雅虎的时候,发现用第三方工具截取不到客户端与服务端的通讯,以前重来没碰到过这种情况,仔细看了看,它的url请求时基于https的,gg了下发现原来https协议和http有着很大的区别.总的来说, ...
- Beginning Scala study note(9) Scala and Java Interoperability
1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class B ...
- Redis 数据库入门指南
Redis 是一个开源数据库,它使用内存数据结构存储,可作为数据库.缓存和消息代理使用.Redis 支持丰富的数据结构,有:字符串(Strings).哈希(Hashs).列表(Lists).集合(Se ...
- SNMP高速扫描器braa
SNMP高速扫描器braa SNMP(Simple Network Monitoring Protocol,简单网络管理协议)是网络设备管理标准协议.为了便于设备管理,现在联入网络的智能设备都支持 ...