Prim算法的执行非常类似于寻找图的最短通路的Dijkstra算法。Prim算法的特点是集合A中的边总是只形成单棵树。如图5所示,阴影覆盖的边属于正在生成的树,树中的结点为黑色。在算法的每一步,树中的结点确定了图的一个割,并且通过该割的轻边被加进树中。树从任意根结点r开始形成并逐渐生长直至该树跨越了V中的所有结点。在每一步,连接A中某结点到V-A中某结点的轻边被加入到树中,由推论2,该规则仅加大对A安全的边,因此当算法终止时,A中的边就成为一棵最小生成树。因为每次添加到树中的边都是使树的权尽可能小的边,因此上述策略也是贪心的。
有效实现Prim算法的关键是设法较容易地选择一条新的边添加到由A的边所形成的树中,在下面的伪代码中,算法的输入是连通图G和将生成的最小生成树的根r。在算法执行过程中,不在树中的所有结点都驻留于优先级基于key域的队列Q中。对每个结点v,key[v]是连接v到树中结点的边所具有的最小权值;按常规,若不存在这样的边则key[v]=∞。域p[v]说明树中v的“父母”。在算法执行中,GENERIC-MST的集合A隐含地满足:
A={(v,p[v])|vÎV-{r}-Q}
当算法终止时,优先队列Q为空,因此G的最小生成树A满足:
A={(v,p[v])|vÎ V-{r}}
代码如下:
#include<iostream>
#include<iomanip>
#include<list>
using namespace std;
#define UNDEFINE -1
//此处Prim算法的图为无向图
struct Edge
{
int verno; //邻接数组中节点编号
int weight; //权值
Edge* next; //指向下一条边
};
struct Vertex
{
Edge *adj; //所指向的节点所在边
int verno; //邻接数组中节点编号
char key; //关键字
};
struct Graph
{
Vertex *vertexs; //节点数组
int vertexnum; //节点个数
int adjnum; //边数
};
struct PrimEdge
{
char startpoint;
char endpoint;
int weight;
PrimEdge(char _pointx,char _pointy,int _wei) : startpoint(_pointx) , endpoint(_pointy),weight(_wei){};
};
class MSTPrim
{
public:
MSTPrim(char *vertex,int vernum,char adj[][2],int *weight,int adjnum);
void PrimInsert(int source,int dest,int weight);
int PrimFindKey(char key);
int PrimExtractMin(int *key,bool *visited,int length);
void PrimSpanTree();
void PrimSpanTreeOutput();
private:
list<PrimEdge> primedge;
Graph *primGraph;
};
MSTPrim::MSTPrim(char *vertex,int vernum,char adj[][2],int *weight,int adjnum)
{
int i,source,dest;
primGraph = new Graph;
primGraph->vertexs = new Vertex[vernum];
primGraph->adjnum = adjnum;
primGraph->vertexnum = vernum;
for(i = 0;i < vernum;i++)
{
primGraph->vertexs[i].key = vertex[i];
primGraph->vertexs[i].verno = i;
primGraph->vertexs[i].adj = NULL;
}
for(i = 0;i < adjnum;i++)
{
source = PrimFindKey(adj[i][0]);
dest = PrimFindKey(adj[i][1]);
PrimInsert(source,dest,weight[i]);
PrimInsert(dest,source,weight[i]); //无向图与有向图的区别在此
}
}
void MSTPrim::PrimInsert(int source,int dest,int weight)
{
if(primGraph->vertexs[source].adj == NULL || primGraph->vertexs[source].adj->weight > weight)
{
Edge* newnode = new Edge;
newnode->verno = dest;
newnode->weight = weight;
newnode->next = primGraph->vertexs[source].adj;
primGraph->vertexs[source].adj = newnode;
}
else
{
Edge* temp = primGraph->vertexs[source].adj;
while(temp->next != NULL) //插入新边的时候,把权值从低到高进行排序
{
if(temp->next->weight > weight)
break;
temp = temp->next;
}
Edge* newnode = new Edge;
newnode->verno = dest;
newnode->weight = weight;
newnode->next = temp->next;
temp->next = newnode;
}
}
int MSTPrim::PrimFindKey(char key)
{
int i;
for(i = 0;i < primGraph->vertexnum;i++)
{
if(primGraph->vertexs[i].key == key)
break;
}
return i;
}
int MSTPrim::PrimExtractMin(int *key,bool *visited,int length)
{
int i,minno;
for(i = 0; i < length;i++) //先找到第一个没被访问的节点
{
if((visited[i] == false)&&(key[i] != UNDEFINE))
break;
}
minno = i;
for(i = minno + 1;i < length;i++)
{
if((visited[i] == false)&&(key[i] != UNDEFINE)&&(key[i] < key[minno]))
minno = i;
}
return minno;
}
void MSTPrim::PrimSpanTree()
{
Edge *temp;
//int weight,starege,endege; //用来记录新增边的开始点结束点与权重
int i,next;
int length = primGraph->vertexnum;
bool *visited = new bool[length];
int *key = new int[length]; //key[v]是所有将v与树中某顶点相连的边中的最小权值
for(i = 0;i < length;i++)
{
visited[i] = false;
key[i] = UNDEFINE;
}
key[0] = 0; //初始化为0,感觉和下面都可以
//key[0] = primGraph->vertexs[0].adj->weight; //初始化第一个数值
//visited[0] = true;
for(i = 0;i < length;i++)
{
next = PrimExtractMin(key,visited,length);
temp = primGraph->vertexs[next].adj;
while(temp != NULL)
{
if(visited[temp->verno] == false)
{
if(((key[temp->verno] > temp->weight)&&(key[temp->verno] != UNDEFINE)) || (key[temp->verno] == UNDEFINE))
key[temp->verno] = temp->weight;
}
else if(visited[next] == false) //由于邻接链表是按照权重从小到达进行排序的,第一个存在边且被访问的节点一定是离已存在节点中最近的边
{
primedge.push_back(*(new PrimEdge(primGraph->vertexs[temp->verno].key,primGraph->vertexs[next].key,temp->weight)));
visited[next] = true;
}
temp = temp->next;
}
visited[next] = true;
}
}
void MSTPrim::PrimSpanTreeOutput()
{
cout<<"The edges of PrimSpanTree :"<<endl;
for(list<PrimEdge>::iterator iter = primedge.begin();iter != primedge.end();iter++)
{
cout<<setw(6)<<iter->startpoint<<setw(6)<<iter->endpoint<<setw(6)<<iter->weight<<endl;
}
}
int main()
{
char vertex[] = {'A','B','C','D','E','F','G','H','I'};
int vernum = 9;
char adj[][2] = {{'A','B'},{'A','H'},{'B','C'},{'B','H'},{'C','D'},{'C','I'},{'C','F'},{'H','I'},{'H','G'},{'G','I'},{'F','G'},{'D','F'},{'D','E'},{'E','F'}};
int weight[] = {4,8,8,11,7,2,4,7,1,6,2,14,9,10};
int adjnum = 14;
MSTPrim *prim = new MSTPrim(vertex,vernum,adj,weight,adjnum);
prim->PrimSpanTree();
prim->PrimSpanTreeOutput();
return 0;
}