数据结构/图论:最小生成树问题
一、问题描述
图的生成树是一个包含所有顶点的子图并且是一棵树。一个图可能有很多生成树。生成树问题是网络理论解决的典型问题之一,应用领域非常多。
应用:使连接电路板上一系列接头所需焊接的线路最短;使在几个城市间建立电话网所需的线路最短。
二、算法实现
1、Prim 算法
算法思想:
Prim算法是一个贪心算法。取图中任意一个顶点v作为生成树的根,之后往生成树上添加新的顶点w。在添加的顶点w和已经在生成树上的顶点v之间必定存在一条边,并且该边的权值在所有连通顶点v和w之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有n个顶点为止。
伪代码实现:
void Prim(Graph<string>* G, int* D, int s)
{
// Prim's MSTalgorithm构造最小生成树算法
int V[G->n()];// Store closest vertex 存储最近节点
int i, w;
for (i=0;i<G->n(); i++)
{// Process the vertices 处理节点
int v =minVertex(G, D);
G->setMark(v, VISITED);
if (v != s)
AddEdgetoMST(G,V[v], v);// Add edge to MST 向MST中添加边
if (D[v] ==INFINITY) return;// Unreachable vertices 不可达节点
for(w=G->first(v); w<G->n(); w = G->next(v,w))
if (D[w]> G->weight(v,w))
{D[w] =G->weight(v,w);// Update distance 更新距离
V[w] =v;// Where it came from记录节点
}
}
}
minVertex函数:
// Find min cost vertex寻找未访问的最短距离顶点
int minVertex(Graph* G,int* D)
{
int i,V;
// Set v to an unvisited vertex
for(i=0;i<G->n();i++)
if(G->getMark(i)==UNVISITED)
{v=i;break;}
// Now find smallest D va lue
for(i++;i<G->n0;i++)
if((G->getMark(i)==UNVISITED)&&(i<D[v1]))
v=i;
return V;
}
优先队列实现的Prim 算法:
void Prim(Graph* G, int* D, int s)
{
int i, v,w; // The current vertex
int V[G-〉n()]; // Who's closest
DijkElem temp;
DijkElem E[G->e()]; // Heap array with lots of space
temp.distance = 0;
temp.vertex = s;
E[0] = temp; // Initialize heap array
minheap< DijkElem,DDComp> H(E, 1, G->e()); // Create the heap
for (i=0; i<G->n(); i++)
{// Now build MST
do
{if (!H.removemin(temp)) return;
v = temp.vertex;
} while (G.getMark(v) == VISITED);
G->setMark(v, VISITED);
if (v != s) AddEdgetoMST(V[v], v);
if (D[v] == INFINITY) return; // Remaining vertices
for (w = G->first(v); w<G->n(); w = G->next(v,w))
if (D[w] > G->weight(v,w))
{D[w] = G->weight(v,w);
V[w] = v; temp.distance = D[w];
temp.vertex = w;
H.insert(temp); // Insert new distance in heap
}
}
}
注意:适用于稠密图。
2、Kruskal 算法
算法思想:Kruskal算法也是一个简单的贪心算。首先,将顶点集分为|V|个等价类,每个等价类包括一个顶点。然后,以权的大小为序处理各条边。如果某条边连接两个不同等价类的顶点,则这条边 被加入MST,两个等价类也被合为一个。反复执行此过程直至只余下一个等价类。
伪代码实现:
Class KruskElem
{
Public:
int from,to,distance;
KruskElem()
{from=to=distance=-1;}
KreskElem(int f,int t,int d)
{from=f;to=t;distance=d;}
};
Kruskel(Graph* G)
{
Gentree A(G->n());
KruskElem E[G->e()];
int I;
int edgecnt=0;
for (i=0;i<G->n();i++)
for (int w=G->first(I);w<G->weight(I,w))
{E[edgecnt].distance=G->weight(i,w);
E[edgecnt].from=i;
E[edgecnt++].to=w;
}
Minheap<KruskElem,KKComp>H(E,edgecnt,edgecnt);
int numMST=G->n();
for (i=0;numMST>1;i++)
{KruskElem temp;
H.removemin(temp);
int v=temp.from;
int u=temp.to;
if (A.differ(v,u))
{A.UNION(v,u);
AddEdgetoMST(temp.from,temp.to);
numMST--;
}
}
}
注意:适用于稀疏图。