最小生成树
Prim算法
lowcost
数组表示的是:还未被加入生成树链的顶点到已生成的最小树链(已加入顶点的集合)的最小值。(与最短路径有所区别)
/** Prim算法过程简介:
* Prim以顶点作为遍历对象,适用于邻接矩阵图(稠密图),即点少线多
* 使用lowcost记录当前已遍历的顶点的权值,每一位对应该顶点在已遍历过程中出现的最小权值
* lowcost初始化即任意选一顶点(一般0号顶点)作起始,将邻接矩阵图中该顶点与其它顶点的权值按序填入,该顶点本身权值为0
* 遍历初始化后的lowcost,找到最小权值min和对应顶点序号k
* 在lowcost中将已遍历的顶点对应位置0,用于过滤已遍历的顶点(起始点本身权值为0)
* 在邻接矩阵图中,k位顶点(第k行)按对应位将较小的权值填入lowcost
* 遍历lowcost找到最小权值min和对应位置k,然后将k位置0
* 依此类推每次循环遍历一个顶点,直到遍历全部顶点
*
* adjvex用于记录lowcost所记录权值的对应连线关系:顶点i与顶点adjvex[i]的权值为lowcost[i]
* 每次替换lowcost中较小的权值时,对应adjvex位也要变更,从而在找到lowcost最小权值时知道连线信息
* 通过每次(n-1次)循环打印一次连线信息,构建一个完整的最小生成树
*/
void MiniSpanTree_Prim(MGtaph G)
{
int min, i, j, k;
int adjvex[MAXVEX]; // 记录权值的对应边
int lowcost[MAXVEX]; // 保存已遍历顶点的边的较小权值
lowcost[0] = 0; // 选择起始根开始遍历
adjvex[0] = 0;
// 对起始顶点进行相关初始化操作
for( i = 1; i < G.numVertexes; i++)
{
lowcost[i] = G.arc[0][i];
adjvex[i] = 0;
}
// 开始最小生成树构建
for( i = 1; i < G.numVertexes; i++)
{
min = INFINTY; // 初始化最小权值为无穷值,表示不连通
j = 1;
k = 0;
// 遍历全部顶点
while(j < G.numVertexes)
{
// 找出lowcost数组已存储的最小权值
if(lowcost[j] != 0 && lowcost[j] < min)
{
min = lowcost[j];
k = j; // 将发现的最小权值的下标存入k,以待使用
}
j++;
}
// 打印当前找到的权值最小的边
printf("(%d,%d)", adjvex[k], k);
lowcost[k] = 0; // 将当前顶点的权值设置为0,方便条件过滤
// 对k顶点的剩余顶点进行遍历,记录较小权值和对应位信息
for( j = 1; j < G.numVertexes; j++)
{
if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j]);
{
lowcost[j] = G.arc[k][j];
adjvex[j] = k;
}
}
}
}
Kruskal算法
/** Kruskal算法过程简介:
* Kruskal算法以边作为遍历对象,适用于稀疏图(邻接表),即点多边少
* edges数组将图的边集按权值排序,然后从最小的边开始遍历
* parent数组记录已遍历边的顶点的集合
* parent数组表示,i顶点可以直接或间接的与parent[i]顶点连通,即第i位的值所代表的顶点与i顶点互通
* 对于新的i顶点,若parent[i]非0,表明i顶点已经在一个链内,可以连通parent[parent[i]]等等
* 由于parent数组中边的关系是单向记录的,所以可以找到已通链的另一端,然后将新顶点记录进去
* 如果一条边的两个顶点都找到了同一个链端,则表明这两个顶点都已经在这条链中存在,如果加上这条边就会有环存在,即顶点被重复添加,此时应不操作
*/
// Kruskal算法
int Find(int *parent, int f)
{
while(parent[f] > 0)
{
f = parent[f];
}
return f;
}
void MiniSpanTree_Kruskal(MGraph G)
{
int i, n, m;
Edge edges[MAXEDGE];
/*记录边的集合
typedef struct _Edge {
int begin;
int end;
int weight;
} Edge;
// 按权值大小排序,过程省略
*/
int parent[MAXVEX]; // 记录已遍历顶点的集合
for(i = 0; i < G.numVertexes; i++)
{
parent[i] = 0;
// 初始化
}
for(i = 0; i < G.numEdges; i++)
{
// 对每一条边,从权值最小开始遍历,逐步构建parent数组
n = Find(parent, edges[i].begin);
// Find函数找到parent互通链的另一端出口
// 如果不在parent链中(包括空parent),直接返回自己,独起一链
m = Find(parent, edges[i].end);
// 如果n==m,表明当前边的两个顶点都已经在parent中的同一个互通链集合中
// 如果n和m分别在parent中不同且不互通的链中,那么m,n肯定不会相等
if(n != m)
{
parent[n] = m; // 将nm互通记录在parent中,表示此顶点已经在生成树的局部链集合中
printf("(%d, %d) %d", edges[i].begin, edges[i].end, edges[i].weight);
}
}
}