普林姆算法

普里姆算法（Prim's algorithm）是图论中的一种贪心算法，可在一个加权连通图中找到其最小生成树。意即由此算法搜索到的边子集所构成的树中，不但包括了连通图里的所有顶点，且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家Vojtěch Jarník发现；并在1957年由美国计算机科学家罗伯特·C·普里姆独立发现；1959年，艾兹格·迪科斯彻再次发现了该算法。因此，在某些场合，普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆－亚尔尼克算法。

**描述**
从单一顶点开始，普里姆算法按照以下步骤逐步扩大树中所含顶点的数目，直到遍及连通图的所有顶点。
# 输入：一个加权连通图，其中顶点集合为，边集合为；
# 初始化：，其中为集合中的任一节点（起始点），；
# 重复下列操作，直到：
## 在集合中选取权值最小的边，其中为集合中的元素，而则是中没有加入的顶点（如果存在有多条满足前述条件即具有相同权值的边，则可任意选取其中之一）；
## 将加入集合中，将加入集合中；
# 输出：使用集合和来描述所得到的最小生成树。

**时间复杂度**
通过邻接矩阵图表示的简易实现中，找到所有最小权边共需的运行时间。使用简单的二叉堆与邻接表来表示的话，普里姆算法的运行时间则可缩减为，其中为连通图的边集大小，为点集大小。如果使用较为复杂的斐波那契堆，则可将运行时间进一步缩短为，这在连通图足够密集时（当满足条件时），可较显着地提高运行速度。

**例示**

**证明**
已知图G的边数量为numEdge, 顶点数量为numVert, prim生成的树为T0, 最小生成树（MST）为Tmin
则有，cost(Tmin)0)
设：
T0 的 numVert-1 条边按照权重由小到大排列依次为：ek1, ek2, ek3, ..., ekn
Tmin 的 numVert-1 条边按照权重由小到大排列依次为：eg1, eg2, eg3, ..., egn
其中n=numVert-1
两棵树的边从小到大权重比较，设第一个属于 T0 但不属于 Tmin 的边为 ed1, 连接该边的两个顶点为 (vs, ve1)
同时存在第一个属于 Tmin 但不属于 T0 且以vs为顶点的边，记为 ed2, 连接该边的两个顶点为 (vs, ve2)。
两个边的起点相同。由Prim算法性质可知，w(ed2) >= w(ed1)
此时，在 Tmin 中删除 ed2 ，添加 ed1，边的数量和顶点数量均不变，且不存在环，因此得到新的生成树Tnew,且cost(Tmin)>=cost(Tnew)
又因为 Tmin 是MST 所以 cost(Tmin)=cost(Tnew)。
以此类推，cost(Tmin)=cost(T0)
T0是最小生成树， 得证.

**各语言程序代码**

**Pascal语言程序**
部分主程序段：
procedure prim(v0:integer);
var
lowcost,closest:array1..maxn of integer;
i,j,k,min,ans:integer;
for i:=1 to n do
begin
lowcosti:=costv0,i;
closesti:=v0;
end;
for i:=1 to n-1 do
begin
min:=maxint;
for j:=1 to n do
if (lowcostj0) then
begin
min:=lowcostj;
k:=j;
end;
inc(ans, lowcostk);
lowcostk:=0;
for j:=1 to n do
if costk,j

**C语言代码**
//来源：严蔚敏 吴伟民《数据结构(C语言版)》
void MiniSpanTree_PRIM (MGraph G, VertexType u) {
/*  用普利姆算法从第u个顶点出发构造网G 的最小生成树T,输出T的各条边。
记录从顶点集U到V-U的代价最小的边的辅助数组定义：
struct
{
VertexType adjvex;
VRtype lowcost;
}closedgeMAX_VERTEX_NUM;
*/

k = LocateVex(G, u);
for (j = 0 ; j 0,Vi∈V-U}
printf(closedgek.adjvex, G.vexsk);    //输出生成树的边
closedgek.lowcost = 0;             //第k条边并入U集
for (j = 0; j 
//来源： 浙大-陈越 《数据结构》
#define ERROR -1
Vertex FindMinDist( MGraph Graph, WeightType dist )
{
/* 返回未被收录顶点中dist最小者 */

Vertex MinV, V;
WeightType MinDist = INFINITY;
for (V=0; VNv; V++) {
if ( distV!=0 && distVNv; V++) {
/* 这里假设若V到W没有直接的边，则Graph->GVW定义为INFINITY */
distV = Graph->G0V;
parentV = 0; /* 暂且定义所有顶点的父结点都是初始点0 */
}
TotalWeight = 0; /* .
..........初始化权重和     */
VCount = 0;      /* 初始化收录的顶点数 */
/* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
MST = CreateGraph(Graph->Nv);
E = (Edge)malloc( sizeof(struct ENode) ); /* 建立空的边结点 */
/* 将初始点0收录进MST */
dist0 = 0;
VCount ++;
parent0 = -1; /* 当前树根是0 */
while (1) {
V = FindMinDist( Graph, dist );
/* V = 未被收录顶点中dist最小者 */
if ( V==ERROR ) /* 若这样的V不存在 */
break;   /* 算法结束 */
/* 将V及相应的边收录进MST */
E->V1 = parentV;
E->V2 = V;
E->Weight = distV;
InsertEdge( MST, E );
TotalWeight += distV;
distV = 0;
VCount++;
for( W=0; WNv; W++ ) /* 对图中的每个顶点W */
if ( distW!=0 && Graph->GVWGVW GVW; /* 更新distW */
parentW = V; /* 更新树 */
}
}
} /* while结束*/
if ( VCount Nv ) /* MST中收的顶点不到|V|个 */
TotalWeight = ERROR;
return TotalWeight;   /* 算法执行完毕，返回最小权重和或错误标记 */
}

**Python语言实现**
此份源码使用了堆优化
from queue import PriorityQueue as priority_queue
from math import inf
class Node:
def __init__(self,id,**kwargs):
self.id = id
self.fst = self.lst = None
def __iter__(self):
return NodeIterator(self)
def __repr__(self):
return "Node(%d)"%self.id
class NodeIterator:
def __init__(self,Node):
self.prst = Node.fst
def __next__(self):
if self.prst == None:
raise StopIteration()
ret = self.prst
self.prst = self.prst.nxt
return ret
class Edge:
def __init__(self,fr,to,**kwargs):
if fr.fst == None:
fr.fst = self
else:
fr.lst.nxt = self
fr.lst = self
self.to = to
self.nxt = None
self.w = 1 if 'w' not in kwargs else kwargs'w'
def __repr__(self):
return "Edge({},{},w = {})",format(self.fr,self.to,self.w)
class Graph:
def __init__(self,V):
self.nodecnt = V
self.nodes = Node(i) for i in range(V)
self.edges = 
def add(self,u,v,**kwargs):
self.edges.append(Edge(self.nodesu,self.nodesv,**kwargs))
def MST_prim(self,begin):

prim algorithm on a graph(with heap),
returns the weight sum of the tree
or -1 if impossible

q = priority_queue()
vis = False for _ in range(self.nodecnt)
q.put((0,begin))
ret = 0
while not q.empty():
prst = q.get()
if visprst1:
continue
visprst1 = True
ret += prst0
for i in self.nodesprst1:
if not visi.to.id:
q.put((i.w,i.to.id))
if all(vis):
return ret
else:
return -1

**Java语言实现**
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Prim {
public static List vertexList = new ArrayList();//结点集
public static List EdgeQueue = new ArrayList();//边集
public static List newVertex = new ArrayList();//已经 访问过的结点
public static void main(String args) {
primTree();
}
public static void buildGraph() {
Vertex v1 = new Vertex("a");
Prim.vertexList.add(v1);
Vertex v2 = new Vertex("b");
Prim.vertexList.add(v2);
Vertex v3 = new Vertex("c");
Prim.vertexList.add(v3);
Vertex v4 = new Vertex("d");
Prim.vertexList.add(v4);
Vertex v5 = new Vertex("e");
Prim.vertexList.add(v5);
addEdge(v1, v2, 6);
addEdge(v1, v3, 7);
addEdge(v2, v3, 8);
addEdge(v2, v5, 4);
addEdge(v2, v4, 5);
addEdge(v3, v4, 3);
addEdge(v3, v5, 9);
addEdge(v5, v4, 7);
addEdge(v5, v1, 2);
addEdge(v4, v2, 2);
}
public static void addEdge(Vertex a, Vertex b, int w) {
Edge e = new Edge(a, b, w);
Prim.EdgeQueue.add(e);
}
public static void primTree() {
buildGraph();
Vertex start = vertexList.get(0);
newVertex.add(start);
for (int n = 0; n 

**参考**
普林演算法与迪科斯彻演算法的策略相似。