算法导论第二十三章 讲解如何求最小生成树
Prim算法是求MST的经典算法之一。
首先,关于MST有伪代码如下
GENERIC-MST(G, w)
1. Set <strong>A</strong> to Empty
2. while <strong>A</strong> does not form a spanning tree
3. find an edge(u, v) that is safe for <strong>A</strong>
4. <strong>A</strong> = Union(<strong>A</strong>, (u, v))
4 . return <strong>A</strong>
Prim算法与Kruskal算法不同在与,Prim算法中A总是构成一棵树。算法的每一步在连接集合A和A之外的结点的 所有边中,选择一天light edge 加入A中。根据推论23.2,这条规则所加入的边都是对A安全的边。
MST-PRIM(G, w, r)
1. for each u belongs to G.V
2. u:key = INFINITE
3. u:pi = NIL
4. r:key = 0
5. Q = G.V
6. while Q is not empty
7. u = EXTRACT-MIN(Q)
8. for each v belongs to G.Adj[u]
9. if v belongs to Q and w(u, v) < v.key
10. v.pi = u
11. v.key = w(u, v)
为了有效地实现Prim算法, 需要一种快速的方法来选择一条新的边,以便加入到由集合A中的边所构成的树里。在算法的执行过程中,所有不在树A中的结点都存放在一个基于key属性的最小优先队列Q中。key属性保存的是该结点与树中结点的所有边中最小边的权重。
在实际的实现中,对于算法的第九行,如何迅速地找到v.key
和第十一行,如何改变v.key在优先队列的值。
C++并没有提供decrease_key操作,所以我的实现是通过加入边来实现。
具体如下
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
struct Edge
{
Edge(size_t u, size_t v, int weight)
{
this->u = u;
this->v = v;
this->weight = weight;
}
size_t u;
size_t v;
int weight;
bool operator>(const Edge &rhs) const
{
return weight > rhs.weight;
}
};
class EdgeCom
{
public:
bool operator()(const Edge& lhs, const Edge& rhs) const
{
return lhs > rhs;
}
};
class Prim
{
public:
Prim(size_t, size_t);
void addEdge(size_t, size_t, int);
void run();
void showResult();
private:
priority_queue<Edge, vector<Edge>, EdgeCom > Q;
vector<vector<int> > map;
size_t V,E;
int totalWeight;
vector<Edge> treeEdge;
vector<bool> onQueue;
};
Prim::Prim(size_t v, size_t e):map(v, vector<int>(v, INT_MAX)), onQueue(v, true)
{
this->V = v;
this->E = e;
totalWeight = 0;
}
void Prim::addEdge(size_t u, size_t v, int weight)
{
map[u][v] = weight;
map[v][u] = weight;
}
void Prim::run()
{
size_t start = 0;
onQueue[start] = false;
size_t cnt = 1;
for(size_t i = 0;i<V;i++)
{
if(map[start][i] != INT_MAX)
{
Q.push(Edge(start, i, map[start][i]) );
}
}
while(cnt < V)
{
Edge x = Q.top();
Q.pop();
size_t cur;
if( onQueue[x.u] == true )
{
cur=x.u;
}
else if(onQueue[x.v] == true )
{
cur=x.v;
}
else
{
continue;
}
onQueue[cur] = false;
treeEdge.push_back(x);
totalWeight += x.weight;
for(size_t i = 0;i<V;i++)
{
if(map[cur][i] != INT_MAX)
{
Q.push(Edge(cur, i, map[cur][i]) );
}
}
/*size_t u = x.u;
size_t v = x.v;
if( onQueue[u] == true )
{
onQueue[u] = false;
treeEdge.push_back(x);
totalWeight += x.weight;
for(size_t i = 0;i<V;i++)
{
if(map[u][i] != INT_MAX)
{
Q.push(Edge(u, i, map[u][i]) );
}
}
}
else if (onQueue[v] == true )
{
onQueue[v] = false;
treeEdge.push_back(x);
totalWeight += x.weight;
for(size_t i = 0;i<V;i++)
{
if(onQueue[i]==true && map[v][i] != INT_MAX)
{
Q.push(Edge(v, i, map[v][i]) );
}
}
}
else
{
continue;
}*/
cnt++;
}
}
void Prim::showResult()
{
cout<<totalWeight<<endl;
cout<<"u\tv\tweight"<<endl;
for(size_t i = 0;i<V-1;i++)
{
cout<<treeEdge[i].u<<"\t"<<treeEdge[i].v<<"\t"<<treeEdge[i].weight<<endl;
}
}
int main()
{
int V, E, u, v, weight;
cin>>V>>E;
Prim p(V, E);
for(size_t i=0;i<E;i++)
{
cin>>u>>v>>weight;
p.addEdge(u,v,weight);
}
p.run();
p.showResult();
cout<<"EOF"<<endl;
return 0;
}