图及其邻接矩阵
寻找该图的最小生成树(包括原图的n个顶点,n-1条边)
普里姆(Prime)算法
实现步骤
1、从顶点入手,假设以V0顶点作为该图最小生成树的根节点。
2、V0顶点有两条边其权值分别为10和11,我们选择最小权值的边,即选中V1顶点。
3、接下来在V0和V1的边中寻找最小权值的边,为11,即选中V5顶点。
4、同理,依次选中V8、V2、V6顶点。
5、此时再寻找最小权值的边时为(V5,V6)其权值为17。但此时V5已经通过V0、V1与V6连通了,因此选择权值为19的(V6, V7),即选中V7顶点。
6、同理,之后依次选择V4、V3顶点。
代码实现
/*
* @Author: Xyh4ng
* @Date: 2022-10-29 18:32:06
* @LastEditors: Xyh4ng
* @LastEditTime: 2022-10-29 21:30:38
* @Description: 普里姆算法
* Copyright (c) 2022 by Xyh4ng 503177404@qq.com, All Rights Reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#define INFINITY 1000
typedef struct Graph
{
int (*adjMatrix)[9];
int data[9];
int numNodes, numEdges;
} Graph;
void Prime(Graph *G)
{
int k = 0;
int lowCost[9]; // 用来记录已选中顶点与各个未选中顶点的最小权值(为0说明该索引顶点已经被选中)
int adjVex[9]; // 用来记录该索引顶点与哪个顶点相通
lowCost[0] = 0; // 先选中v0顶点
adjVex[0] = 0;
printf("%d", G->data[0]);
for (int i = 1; i < G->numNodes; i++)
{
lowCost[i] = G->adjMatrix[0][i];
adjVex[i] = 0;
}
for (int i = 1; i < G->numNodes; i++)
{
// 选择出可选边中的最小权值,并将新增顶点的索引赋值给k
int min = INFINITY;
for (int j = 1; j < G->numNodes; j++)
{
if (lowCost[j] != 0 && lowCost[j] < min)
{
min = lowCost[j];
k = j;
}
}
printf("%d", G->data[k]);
lowCost[k] = 0;
// 遍历新增顶点的边,并更新已选中顶点与未选中顶点相通的最小权值的边
for (int j = 1; j < G->numNodes; j++)
{
if (lowCost[j] != 0 && G->adjMatrix[k][j] < lowCost[j])
{
lowCost[j] = G->adjMatrix[k][j];
adjVex[j] = k;
}
}
}
}
int main()
{
Graph *G = (Graph *)malloc(sizeof(Graph));
G->numNodes = 9;
G->numEdges = 15;
for (int i = 0; i < G->numNodes; i++)
{
G->data[i] = i;
}
int edgeMatrix[9][9] = {
{0, 10, INFINITY, INFINITY, INFINITY, 11, INFINITY, INFINITY, INFINITY},
{10, 0, 18, INFINITY, INFINITY, INFINITY, 16, INFINITY, 12},
{INFINITY, 18, 0, 22, INFINITY, INFINITY, INFINITY, INFINITY, 8},
{INFINITY, INFINITY, 22, 0, 20, INFINITY, 24, 16, 21},
{INFINITY, INFINITY, INFINITY, 20, 0, 26, INFINITY, 7, INFINITY},
{11, INFINITY, INFINITY, INFINITY, 26, 0, 17, INFINITY, INFINITY},
{INFINITY, 16, INFINITY, 24, INFINITY, 17, 0, 19, INFINITY},
{INFINITY, INFINITY, INFINITY, 16, 7, INFINITY, 19, 0, INFINITY},
{INFINITY, 12, 8, 21, INFINITY, INFINITY, INFINITY, INFINITY, 0}};
G->adjMatrix = edgeMatrix;
Prime(G);
return 0;
}
克鲁斯卡尔(Kruskal)算法
实现步骤
1、从边入手,将图的所有边按照从小到大的顺序排列。
2、依次挑选出权值最小的边。
3、注意:要避免挑选的边和已经挑选出的边形成环,此时要舍弃当前挑选的边。
4、直到所有顶点都被选择并连通,则停止。
代码实现
/*
* @Author: Xyh4ng
* @Date: 2022-10-29 21:32:39
* @LastEditors: Xyh4ng
* @LastEditTime: 2022-10-29 22:13:26
* @Description: 克鲁斯卡尔算法
* Copyright (c) 2022 by Xyh4ng 503177404@qq.com, All Rights Reserved.
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct Edge
{
int begin;
int end;
int weight;
} Edge;
int FindEnd(int *adjvex, int b)
{
while (adjvex[b] > 0)
{
b = adjvex[b];
}
return b;
}
void KrusKal(Edge G[])
{
int adjvex[9] = {0}; // 用于记录那些边被选中(begin:index, end:value)
// 遍历每一条边
for (int i = 0; i < 15; i++)
{
int begin = FindEnd(adjvex, G[i].begin);
int end = FindEnd(adjvex, G[i].end);
if (begin != end) // 不会和已选边构成环
{
adjvex[begin] = end;
printf("(%d, %d) %d\n", G[i].begin, G[i].end, G[i].weight);
}
}
}
int main()
{
Edge G[15] = {{4, 7, 7},
{2, 8, 8},
{0, 1, 10},
{0, 5, 11},
{1, 8, 12},
{3, 7, 16},
{1, 6, 16},
{5, 6, 17},
{1, 2, 18},
{6, 7, 19},
{3, 4, 20},
{3, 8, 21},
{2, 3, 22},
{3, 6, 24},
{4, 5, 26}};
KrusKal(G);
return 0;
}
总结
对比两个算法,克鲁斯卡尔算法主要是针对边来展开,边数少时效率会非常高,所以对于稀疏图有很大的优势;而普里姆算法对于稠密图,即边数非常多的情况会更好一些。