bobodesu

今天花了很多时间去实现学校布置的作业,所以我突然想到我是否可以将这些经历写到网络上,以便更好的分享出去

本次实现的具体内容是:用C语言实现邻接矩阵存储的无向图,判断是否为连通图,并且实现最小生成树Prim算法

(引用的话不重要)在此之前已经有过一段开发基础了,但大部分都是用C++和其他语言,而用C语言很少,主要原因是其太灵活并且自身觉得困难,努力完成了上述内容后,我忽然发现其实也还好,无非就是需要思考的事情多了。

参考上述要求,我使用了并查集实现图是否为连通图,使用了堆排序(优先队列)实现了Prim算法。其实这些在C++当中很简单,但是使用C语言就很麻烦,需要手动实现。具体整体逻辑如下

  1. 无向图部分

使用结构体定义
其中有很多实现方法。其中最核心的是UG_IsConnectedGraph()是否为连通图、UG_GetMST()获取最小生成树。这俩分别使用到了并查集与堆排序。下方是无向图部分代码,并查集和堆排序在后面

/*以下无向图定义*/
const int INF = 1e9; //代表邻接表未连接
struct UG { //无向带权图 Undirected Graph
	int n;//元素个数
	int* s; //邻接矩阵,动态创建
};
typedef UG* UGPoi;

UGPoi UG_Init(int n);//创建无向图
void UG_Link(UGPoi ug, int p, int q, int val);//将两条边连接起来
void UG_PrintGraph(UGPoi ug);//打印图(邻接矩阵)
bool UG_IsConnectedGraph(UGPoi ug);//是否是连通图
UGPoi UG_GetMST(UGPoi ug);//获取最小生成树
struct __UG_HeapNode { //UG_GetMST()函数中需使用到的类型
        //边长,目标结点,原始节点
	int len, poi, fro; 
};
void* __UG_getheapnode(int fro, int poi, int len);//创建__UG_HeapNode结构体
bool __UG_heapcompare(void* a, void* b);//堆排序比较函数




/*其中实现如下*/
UGPoi UG_Init(int n) {
	//申请UG结构体内存
	UGPoi ug = (UGPoi)malloc(sizeof(UG));
	//为s创建n*n的邻接矩阵空间
	ug->s = (int*)malloc(sizeof(int) * n * n);
	for (int i = 0;i < n;i++)for (int j = 0;j < n;j++)
		ug->s[i * n + j] = INF;
	for (int i = 0;i < n;i++)ug->s[i * n + i] = 0;
	ug->n = n;
	return ug;
}
void UG_Link(UGPoi ug, int p, int q, int val) {
	p--;q--;
	ug->s[p * ug->n + q] = val;
	ug->s[q * ug->n + p] = val;
}
void UG_PrintGraph(UGPoi ug) {
	printf("(∞表示没有连接)\n");
	int n = ug->n;
	for (int i = 0;i < n;i++) {
		for (int j = 0;j < n;j++) {
			if (ug->s[i * n + j] == INF) printf(" ∞ ");
			else  printf("%3d ", ug->s[i * n + j]);
		}
		printf("\n");
	}
}
bool UG_IsConnectedGraph(UGPoi ug) {
	//使用并查集来判断是否为连通图
	int n = ug->n;
	int rest = n;
	int* uf = UnionFind_Init(n);
	for (int i = 0;i < n;i++)
		for (int j = i + 1;j < n;j++)
			if (ug->s[i * n + j] != INF && 
				UnionFind_Find(uf, i+1)!=UnionFind_Find(uf, j+1)
			) {
				UnionFind_Union(uf, i + 1, j + 1);
				rest--;
			}
	if (rest == 1)return 1;
	else return 0;
}
UGPoi UG_GetMST(UGPoi ug) {
	//使用优先队列存放Prim遍历到的结点,每次取出最小的。优先队列使用堆实现
	int n = ug->n;
	UGPoi res = UG_Init(n);
	//创建堆,堆的最大大小与比较函数传进去
	HeapPoi heap = Heap_Init(n * n, sizeof(__UG_HeapNode), __UG_heapcompare);
	//是否已遍历到
	int* isok = (int*)malloc(sizeof(int) * n);
	memset(isok, 0, sizeof(int) * n);
	Heap_Ins(heap, __UG_getheapnode(0, 0, 0));
	while (!Heap_Empty(heap)) {
		__UG_HeapNode* t = (__UG_HeapNode*)malloc(sizeof(__UG_HeapNode));
		memcpy(t, Heap_Top(heap), sizeof(__UG_HeapNode));
		Heap_Pop(heap);

		//如果该节点已经遍历过了,跳过
		if (isok[t->poi])continue;
		isok[t->poi] = 1; //如果没有遍历过,将其设为已遍历,防止后续循环遍历到

		//将其该边加入到res结果当中
		UG_Link(res, t->fro+1, t->poi+1 , t->len);
		
		//找与之相邻的结点,将其加入堆当中
		for (int i = 0;i < n;i++) {
			if (i == t->poi) continue;
			if (ug->s[t->poi * n + i] != INF && isok[i] == 0) {
				__UG_HeapNode* readyins = (__UG_HeapNode*)__UG_getheapnode(t->poi, i, ug->s[t->poi * n + i]);
				Heap_Ins(heap, readyins);
			}
		}
	}
	return res;
}
void* __UG_getheapnode(int fro, int poi, int len) {
	__UG_HeapNode* res = (__UG_HeapNode*)malloc(sizeof(__UG_HeapNode));
	res->len = len;
	res->poi = poi;
	res->fro = fro;
	return (void*)res;
}
bool __UG_heapcompare(void* a, void* b) {
	int p = ((__UG_HeapNode*)a)->len;
	int q = ((__UG_HeapNode*)b)->len;
	return p < q;
}
  1. 并查集部分

在无向图中,UG_IsConnectedGraph()函数中使用到了并查集,代码如下

/*以下为并查集定义*/
int* UnionFind_Init(int n); //创建森林,返回森林首指针
int UnionFind_Find(int* s, int n); //Find查找
void UnionFind_Union(int* s, int a, int b); //Union合并

/*以下为并查集实现*/
int* UnionFind_Init(int n) {
	int* s = (int*)malloc(sizeof(int) * (n + 1));
	for (int i = 0;i <= n;i++) {
		s[i] = i;
	}
	return s;
}
int UnionFind_Find(int *s, int n) {
	if (s[n] != n)return s[n] = UnionFind_Find(s,s[n]);
	return n;
}
void UnionFind_Union(int *s, int a, int b) {
	if (UnionFind_Find(s,a) != UnionFind_Find(s,b)) {
		s[UnionFind_Find(s,a)] = UnionFind_Find(s,b);
	}
}
  1. 堆排序部分

在无向图中,UG_GetMST()函数中使用到了堆排序,代码如下

/*以下为堆定义*/
struct Heap {
	int n; //堆中元素数量
	void* s;//堆数据元素,使用预先申请好的内存线性存储
	bool (*compare)(void* a, void* b);//堆数据元素比较函数
	int node_size;//堆数据元素类型字节大小
};
typedef Heap* HeapPoi;
//初始化堆。需填好可能使用的最大数量、数据元素类型字节大小、数据元素比较函数
HeapPoi Heap_Init(int n, int node_size, bool(*compare)(void* a, void* b)); 
void Heap_Ins(HeapPoi h, void* v);//向堆中添加元素并自动排序
void* Heap_Top(HeapPoi h);//取堆顶
void Heap_Pop(HeapPoi h);//弹出堆顶,并自动排序
bool Heap_Empty(HeapPoi h);//堆是否为空
void* __Heap_getattr(HeapPoi h, int shifting);//取堆中第shifting个数据元素,相当于偏移量
void __Heap_swap(HeapPoi h, int a, int b);//交换两个数据元素


/*以下为堆实现*/
void* __Heap_getattr(HeapPoi h, int shifting) {
	return (void*)((char*)h->s + shifting * h->node_size);
}
void __Heap_swap(HeapPoi h, int a, int b) {
	void* t = malloc(h->node_size);
	memcpy(t, __Heap_getattr(h, a), h->node_size);
	memcpy(__Heap_getattr(h, a), __Heap_getattr(h, b), h->node_size);
	memcpy(__Heap_getattr(h, b), t, h->node_size);
}
HeapPoi Heap_Init(int n, int node_size, bool(*compare)(void *a,void *b)) {
	HeapPoi h = (HeapPoi)malloc(sizeof(Heap));
	h->compare = compare;
	h->s = (void*)malloc(node_size * n);
	h->node_size = node_size;
	h->n = 0;
	return h;
}
void Heap_Ins(HeapPoi h, void* v) {
	memcpy(__Heap_getattr(h, h->n), v, h->node_size);
	h->n++;
	int p = h->n - 1;
	while (p > 0) {
		//获取到 p的双亲结点
		int f = (p & 1) ? p / 2 : (p - 1) / 2;
		//判断s[p]是否不小于s[f],如果是break
		if (!h->compare(__Heap_getattr(h, p), __Heap_getattr(h, f))) break;
		//将s[p]和s[f]交换
		__Heap_swap(h, p, f);
		p = f;
	}
}
void* Heap_Top(HeapPoi h){
	return __Heap_getattr(h, 0);
}
void Heap_Pop(HeapPoi h) {
	h->n--;
	if (h->n == 0)return;
	//将s[0]替换为s[n]
	memcpy(__Heap_getattr(h, 0), __Heap_getattr(h, h->n), h->node_size);
	int p = 0;
	while (1) {
		int son = -1;
		if (p * 2 + 1 < h->n) son = p * 2 + 1;
		if (p * 2 + 2 < h->n && h->compare(__Heap_getattr(h, p*2+2), __Heap_getattr(h, son) ) ) {
			son = p * 2 + 2;
		}
		if (son == -1 || h->compare(__Heap_getattr(h, p), __Heap_getattr(h, son)))break;
		__Heap_swap(h, son, p);
		p = son;
	}
}
bool Heap_Empty(HeapPoi h) {
	return h->n == 0;
}

相关文章: