普林姆算法

普里姆算法（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 

**參考**
普林演算法與迪科斯徹演算法的策略相似。