Prim算法的特点是集合E中的边总是形成单棵树,树从任意根节点s开始形成,
并逐渐生成,直至该树覆盖了V中的所有顶点。
设图G =(V,E),其生成树的顶点集合为U。
①.把v0放入U
②.在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树
③.把②找到的边的v加入U集合。如果U集合已有n个元素,则结束,否则继续执行②
Challenge: Find the min weight edge with exactly one endpoint in T
Lazy solution: Maintain a PQ of edges with (at least) one endpoint in T
(1)key=edge; priority=weight of edge
(2)delete-min to determine next edge e=(u,v) to add to T
(3)disregard if both endpoints u and v are in T
(4)otherwise, let u be the vertex not in T
add to PQ any edge incident to u(assue other endpoint not in T)
add u to T
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <list>
#include <vector>
#include <queue>
#include <string>
using std::list;
using std::vector;
using std::queue;
using std::priority_queue;
using std::string;
using std::cout;
using std::endl;
class Edge
{
public:
Edge(int u, int v, double w)
{
if (u >= 0 && v >= 0)
{
this->u = u;
this->v = v;
this->weight = w;
}
}
double getWeight() { return weight; }
int either() { return u; }
int other(int p)
{
if (p == u)
return v;
else if (p == v)
return u;
}
string toString()
{
string str;
char result[64] = "";
sprintf(result, "%d -- %d : %8.3f\n", u, v, weight);
str = result;
return str;
}
private:
int u;
int v;
double weight;
};
class EdgeCmp
{
public:
bool operator() (Edge *p, Edge *q)
{
return p->getWeight() > q->getWeight();
}
};
class EdgeWeightedGraph
{
public:
EdgeWeightedGraph(int nVertex, int nEdge, int arr[][2], double weight[]);
~EdgeWeightedGraph();
int getV() { return V; }
list<Edge *> edges();
list<Edge *> getAdj(int v);
private:
int V;
int E;
list<Edge *> *adj;
};
EdgeWeightedGraph::EdgeWeightedGraph(int nVertex, int nEdge, int arr[][2], double weight[])
{
V = nVertex;
E = nEdge;
adj = new list<Edge *>[nVertex];
for (int i = 0; i < E; ++i)
{
Edge *e = new Edge(arr[i][0], arr[i][1], weight[i]);
adj[arr[i][0]].push_back(e);
adj[arr[i][1]].push_back(e);
}
}
EdgeWeightedGraph::~EdgeWeightedGraph()
{
list<Edge *> ls = edges();
for (list<Edge *>::iterator it = ls.begin(); it != ls.end(); ++it)
{
delete *it;
*it = NULL;
}
delete []adj;
adj = NULL;
}
list<Edge *> EdgeWeightedGraph::getAdj(int v)
{
if (v >= 0 && v < V)
return adj[v];
}
list<Edge *> EdgeWeightedGraph::edges()
{
list<Edge *> ls;
for (int i = 0; i < V; ++i)
{
int selfLoops = 0;
list<Edge *> tmpList = getAdj(i);
for (list<Edge *>::iterator it = tmpList.begin(); it != tmpList.end(); ++it)
{
if ((*it)->other(i) > i)
ls.push_back(*it);
else if ((*it)->other(i) == i)//only add one copy of each self loop
{
if (0 == selfLoops%2)
ls.push_back(*it);
++selfLoops;
}
}
}
return ls;
}
class PrimMST
{
public:
PrimMST(EdgeWeightedGraph *g, int s);
~PrimMST();
queue<Edge *> edges() { return mst; }
double getWeight() { return weight; }
private:
void visit(EdgeWeightedGraph *g, int v);
private:
bool *marked; //true:visited, false:unvisited
priority_queue<Edge *, vector<Edge *>, EdgeCmp> pq;
double weight; //weight of mst
queue<Edge *> mst;//edges in mst
};
PrimMST::PrimMST(EdgeWeightedGraph *g, int s)
{
weight = 0;
int v = g->getV();
marked = new bool[v];
memset(marked, false, v);
visit(g, s);
while(!pq.empty())
{
Edge *e = pq.top();
pq.pop();
int u = e->either();
int v = e->other(u);
if (marked[u] && marked[v])
continue;
mst.push(e);
weight += e->getWeight();
if (!marked[u])
visit(g, u);
if (!marked[v])
visit(g, v);
}
}
PrimMST::~PrimMST()
{
if (NULL != marked)
{
delete []marked;
marked = NULL;
}
}
// add all edges e incident to v onto pq if other endpoint has not yet been visited
void PrimMST::visit(EdgeWeightedGraph *g, int v)
{
marked[v] = true;
list<Edge *> ls = g->getAdj(v);
for (list<Edge *>::iterator it = ls.begin(); it != ls.end(); ++it)
if (!marked[(*it)->other(v)])
pq.push(*it);
}
int main()
{
int arr[][2] = { {0, 7}, {2, 3}, {1, 7}, {0, 2},
{5, 7}, {1, 3}, {1, 5}, {2, 7},
{4, 5}, {1, 2}, {4, 7}, {0, 4},
{6, 2}, {3, 6}, {6, 0}, {6, 4}
};
double wgt[] = {0.16, 0.17, 0.19, 0.26, 0.28, 0.29, 0.32, 0.34,
0.35, 0.36, 0.37, 0.38, 0.40, 0.52, 0.58, 0.93};
EdgeWeightedGraph graph(8, 16, arr, wgt);
PrimMST pmst(&graph, 0);
queue<Edge *> edge = pmst.edges();
while (!edge.empty())
{
cout << edge.front()->toString();
edge.pop();
}
cout << "weight: " << pmst.getWeight() << endl;
return 0;
}
0 -- 7 : 0.160
1 -- 7 : 0.190
0 -- 2 : 0.260
2 -- 3 : 0.170
5 -- 7 : 0.280
4 -- 5 : 0.350
6 -- 2 : 0.400
weight: 1.81