C语言实现Prim算法
一些心得放在代码的注释中,建议结合具体的图进行学习。
Prim算法过程描述:
-
在图中挑选任一顶点v0准备扩展;
-
每次扩展一个从未扩展的顶点,要求所连的边必须是已扩展的点连向所有未扩展的点中,边的权值最小的一条。
-
重复步骤2,直至图中所有的顶点都被扩展过。
#include<iostream>
#define INT_MAX 100
using std::cout;
using std::cerr;
using std::endl;
const int MaxInt = INT_MAX;
const int n = 6;
typedef int AdjMatrix[n][n];
typedef struct TreeEdgeNode
{
int fromVex, toVex;
int weight;
}TreeEdgeNode;
typedef TreeEdgeNode MST[n - 1];
void PrimMST(AdjMatrix G, MST T, int rt)
{
//从顶点rt出发构造图G的最小生成树T,rt成为树的根结点
TreeEdgeNode e;
int i, k = 0, min, minpos, v;
for (i = 0; i < n; ++i)
{
if (i != rt)
{
T[k].fromVex = rt;
T[k].toVex = i;
T[k++].weight = G[rt][i];
}
}
for (k = 0; k < n - 1; ++k)
{
//依次求MST的侯选边
min = MaxInt;
for (i = k; i < n - 1; ++i)
{
//遍历当前侯选边集合
if (T[i].weight < min)
{
//选具有最小权值的侯选边
min = T[i].weight;
minpos = i;
}
}
if (min == MaxInt)
{
//图不连通,出错处理
cerr << "Graph is disconnected!" << endl;
exit(0);
}
//将符合要求的代价最小的边换到k所指的位置,
//由于之后对MST表的操作都是在k之后的位置进行,这使得以v为终点的边不会再遍历。
e = T[minpos];
T[minpos] = T[k];
T[k] = e;
v = T[k].toVex;
/**
* k划分MST表。
* k之前的内容表示已经成功找到的满足条件的边,
* 因此修改侯选边集合以及选取具有最小权值的侯选边都是从k之后开始的。
*/
for (i = k + 1; i < n - 1; ++i)
{
//该循环的功能是修改侯选边集合
//将新扩展的顶点作为起点,看它到某一顶点的代价是否小于原表中原来扩展过的顶点到这一点的代价。
if (G[v][T[i].toVex] < T[i].weight)
{
T[i].weight = G[v][T[i].toVex];
T[i].fromVex = v;
}
/**
* 这里非常巧妙。MST表中实际上存在上一步未选中的边,而加入新扩展的结点后,待选择的边就更多了。
* 我们并不需要那些比MST表中边权值更大的边,这么做也节省了下一轮循环中选取最小权值侯选边的时间。
*/
}
}
}
int main()
{
int G[6][6] = { {0, 6, 1, 5, 100, 100},
{6, 0, 5, 100, 3, 100},
{1, 5, 0, 5, 6, 4},
{5, 100, 5, 0, 100, 2},
{100, 3, 6, 100, 0, 6},
{100, 100, 4, 2, 6, 0} };
TreeEdgeNode M[5];
int rt = 0;
PrimMST(G, M, rt);
for (int i = 0; i < 5; ++i)
{
cout << M[i].fromVex << " " << M[i].toVex << " " << M[i].weight << endl;
}
return 0;
}