1.克鲁斯卡尔算法
克鲁斯卡尔算法的核心思想是从边集出发,逐步把代价最小且不与已经加到最小生成树的边集构成回路的边加入到最小生成树的边集中,直到求出构成最小生成树的n-1边(n是图的顶点数)。算法的基本步骤如下。
- 对边集进行排序。
- 从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中。(简单的说就是试探性的加入这条边看加入后是否构成环)。
- 重复2,直至图G中所有的节点都在同一个连通分量中。
非常推荐你去看这个视频!!!
2.具体实现。
以邻接矩阵的形式存储图的关系,测试图和具体代码如下。
测试数据:
9 15
0 1 10
0 5 11
1 2 18
1 8 12
1 6 16
2 8 8
2 3 22
3 8 21
3 6 24
3 7 16
3 4 20
4 7 7
4 5 26
5 6 17
6 7 19
#include <cstdio>
#define MAXVER 20
#define INFINITY 1000000
typedef struct {
int adjacentMatrix[MAXVER][MAXVER];//图的邻接矩阵
int numVertexes, numEdges;//图的顶点的数量,边的数量
} Graph, *pGraph;
typedef struct {
int begin;//边的起点
int end;//边的终点
int weight;//权值
} Edge, *pEdge;
//初始化图
void initGraph(pGraph G) {
scanf("%d%d", &G->numVertexes, &G->numEdges);
//初始化邻接矩阵
for(int i = 0; i < G->numVertexes; i++) {
for(int j = 0; j < G->numVertexes; j++) {
if(i == j) {//到自身的代价为0
G->adjacentMatrix[i][j] = 0;
} else {
G->adjacentMatrix[i][j] = INFINITY;
}
}
}
//输入边的信息
for(int i = 0; i < G->numEdges; i++) {
int rowIndex, colomnIndex;
scanf("%d%d", &rowIndex, &colomnIndex);
scanf("%d", &G->adjacentMatrix[rowIndex][colomnIndex]);
}
//邻接矩阵关于对角线对称完善邻接矩阵
for(int i = 0; i < G->numVertexes; i++) {
for(int j = i; j < G->numVertexes; j++) {
G->adjacentMatrix[j][i] = G->adjacentMatrix[i][j];
}
}
}
//交换两条边的信息
void swap(pEdge one, pEdge two) {
int temp;
temp = one->begin; one->begin = two->begin; two->begin = temp;
temp = one->end; one->end = two->end; two->end = temp;
temp = one->weight; one->weight = two->weight; two->weight = temp;
}
//冒泡排序
void sort(pEdge edges, int len) {
for(int i = 0; i < len; i++) {
for(int j = i + 1; j < len; j++) {
if(edges[i].weight > edges[j].weight) {
swap(&edges[i], &edges[j]);
}
}
}
}
//查找连线顶点的尾部下标,可以沿着parent数组找连通分量
int find(int *parent, int f) {
while(parent[f] > 0) {
f = parent[f];
}
return f;
}
//克鲁斯卡尔算法求最小生成树
void miniSpanTree(Graph G) {
int parent[G.numVertexes];//用来存储已经加到最小生成树的点,用于判断新加的边是否构成回路
for(int i = 0; i < G.numVertexes; i++) {
parent[i] = 0;
}
Edge edges[G.numEdges];
int cnt = 0;
//利用邻接矩阵初始化边集
for (int i = 0; i < G.numVertexes-1; i++)
{
for (int j = i + 1; j < G.numVertexes; j++)
{
if (G.adjacentMatrix[i][j] < INFINITY)
{
edges[cnt].begin = i;
edges[cnt].end = j;
edges[cnt].weight = G.adjacentMatrix[i][j];
cnt++;
}
}
}
sort(edges, G.numEdges);//对边集进行排序
int sum = 0;
printf("最小生成树:\n");
for(int i = 0; i < G.numEdges; i++) {
int n = find(parent, edges[i].begin);
int m = find(parent, edges[i].end);
if(m != n) {//不构成环,该边就可以加入最小生成树中了
parent[n] = m;//将此边的结尾顶点放入下标为起点
printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
sum += edges[i].weight;
}
}
printf("权值和:%d", sum);
}
int main(int argc, char const **argv) {
Graph G;
initGraph(&G);
miniSpanTree(G);
}