C语言无向网的数组表示法储存以及Kruskal算法的实现

PS:本代码实现受此文启发

Kruskal算法代码实现需要两步:
①弧结点按照权重从小到大排序(注意交换的时候是交换结构体)
②判断这个弧能不能成环,如果不能成环,就加入到其中

对于第二点的实现分为了两步,首先有一个Parent整型数组,长度为弧的数目,初始化为0,在程序运行过程中,如果Parent[i]为0,说明这是根或者是一个只有根节点的树,如果不为0,说明有父亲,但不一定是根,所以需要寻找父亲的父亲,直到找到Parent[n] = 0,那么n才是最终的根节点,这就是Find_Root函数的作用。
那么,为什么可以这样判断呢?这就是实现的第二步,每当我们选取一条弧的时候,我们都会把Parent[head] 更新为tail(之前确保了head<tail),这样就让大的值做了root。如果成环,就必定有两个树的root相同,所以我们选择root不同的树加入。

测试图:
在这里插入图片描述
网的内容从文本文档Krusral.txt中读取 文档内容如下
在这里插入图片描述
运行情况:
在这里插入图片描述
代码如下:

/*
	实现内容:
	①无向网的数组表示法储存实现
	②克鲁斯卡尔算法
	数组表示法:分两部分,一个顶点数组,一个邻接矩阵
				两结点间没有连接 用MAX_INT表实
	VS2019编译通过  2020.7.31
*/
#define _CRT_SECURE_NO_WARNINGS
#define FILE_BUFFER_LENGTH 30000
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>

#define ALGraphType char
#define Max_Vertices 100

typedef enum GRAKIND { DG, UG, DN, UN } GraKind;

//图的顶点定义
typedef struct VNODE {
	ALGraphType Date;

}VNode;

typedef struct ALGRAPH {
	VNode* Vertices;
	int Vexnum, Arcnum; //顶点数目、边的数目
	int** Arc_Matrix;
	GraKind Kind;

}ALGraph;

//弧结点 PS:数大的是End
typedef struct _EDGE {
	int Begin;
	int End;
	int Weight;

}Edge;

//根据顶点信息,找到顶点在顺序存储的位置(默认顶点按照字母顺序编号)
int Locate(ALGraphType ch) {
	return toupper(ch) - 'A';
}

//创建图
void Creat_ALGraph(ALGraph* G) {

	if (NULL == G)
		return;

	//从文件中读取数据
	FILE* fp = fopen("C:\\Users\\86132\\Desktop\\第十周周末程序设计\\克鲁斯卡尔算法(邻接矩阵表示法)\\Kruskal.txt", "r");


	fscanf(fp, "%d", &G->Vexnum);
	G->Vertices = (VNode*)malloc(sizeof(VNode) * G->Vexnum);

	fscanf(fp, "%d", &G->Arcnum);
	G->Arc_Matrix = (int**)malloc(sizeof(int*) * G->Vexnum);

	//初始化邻接矩阵
	for (int i = 0; i < G->Vexnum; i++) {
		G->Arc_Matrix[i] = (int*)malloc(sizeof(int) * G->Vexnum);

		//初始赋值MAX_INT
		for (int j = 0; j < G->Vexnum; j++)
			G->Arc_Matrix[i][j] = INT_MAX;
	}

	//录入顶点信息
	for (int i = 0; i < G->Vexnum; i++) {

		//空格作用为不读回车
		fscanf(fp, " %c", &G->Vertices[i].Date);

	}

	//录入每个边的信息(默认顶点按照ABCD编号)
	for (int i = 0; i < G->Arcnum; i++) {

		char ch1, ch2;
		int n;

		空格作用为不读回车
		fscanf(fp, " %c %c%d", &ch1, &ch2, &n);

		//更新邻接矩阵
		ch1 = Locate(ch1);
		ch2 = Locate(ch2);

		G->Arc_Matrix[ch1][ch2] = G->Arc_Matrix[ch2][ch1] = n;
	}
	fclose(fp);
}

//冒泡排序辅助函数
void Exchange(Edge* a, Edge* b) {
	Edge c = *a;
	*a = *b;
	*b = c;
}

//寻找根节点 辅助判断此边能不能加入
int Find_Root(int* Parent, int i) {
	//证明上一级有结点
	while (0 < Parent[i])
		i = Parent[i];
	return i;
}

//克鲁斯卡尔算法 输出图G最小生成树的边
void Mini_Span_Tree_Kruskal(ALGraph G) {

	//从数组表示法得到边的信息
	Edge* Edges = (Edge*)malloc(sizeof(Edge) * G.Arcnum);

	//更新数组Edges的内容
	int tmp = 0;

	for (int i = 0; i < G.Vexnum; i++) {
		//由于是无向网 所以只需要循环一半矩阵就可以
		for (int j = i + 1; j < G.Vexnum; j++) {
			if (INT_MAX != G.Arc_Matrix[i][j]) {

				Edges[tmp].Begin = i;
				Edges[tmp].End = j;
				Edges[tmp++].Weight = G.Arc_Matrix[i][j];
			}
		}
	}

	//权重需要从小到大排序
	//改进版冒泡排序
	for (int i = 0; i < G.Arcnum; i++) {
		//flag记录是否交换 LastPos记录最后一次排序位置
		int flag = 0, LastPos = G.Arcnum, k = G.Arcnum - 1;

		for (int j = 0; j < k; j++) {
			if (Edges[j].Weight > Edges[j + 1].Weight) {
				Exchange(&Edges[j], &Edges[j + 1]);
				flag = 1;
				LastPos = j + 1;
			}
		}
		if (0 == flag)
			break;
		k = LastPos;
	}

	//通过Parent数组来寻找此子树的根结点 0表示自己成根 无儿子
	int* Parent = (int*)malloc(sizeof(int) * G.Vexnum);
	memset(Parent, 0, sizeof(int) * G.Vexnum);
	int num = 1;

	//需要遍历所有的边来寻找最小的n-1个
	for (int i = 0; i < G.Arcnum; i++) {

		//判断这条边会不会成环
		int m = Find_Root(Parent, Edges[i].Begin), n = Find_Root(Parent, Edges[i].End);

		//不会成环 
		if (m != n) {

			//让大的做根
			Parent[m] = n;
			printf("第%d条弧依附的顶点为%c %c,权重为%d\n", num++, G.Vertices[Edges[i].Begin].Date, G.Vertices[Edges[i].End].Date, Edges[i].Weight);
		}
	}

}

int main() {

	ALGraph G;
	Creat_ALGraph(&G);
	Mini_Span_Tree_Kruskal(G);

	return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值