[图] △ Prim算法实现-求连通图的最小生成树(用孩子兄弟CSTree)表示-邻接表(ALGraph)-C语言实现

版权声明:本文为博主原创文章,若有错误之处望大家批评指正!转载需附上原文链接,谢谢! https://blog.csdn.net/summer_dew/article/details/83024544

【题目】使用Prim算法求得图的最小生成树

【解题思路】若对Prim算法不了解,建议先看这篇:Prim,这篇有Prim简单算法实现(只求一个最小生成树的权重之和),掌握了这个之后,比较好理解以下内容

  1. lowCost[v]表示:当前生成树到结点v的最短边
  2. 多设一个数组,lowNode[v]表示:到结点v的最短边的另一个结点是lowNode[v] --> 即表示结点v的父亲是lowNode[v],每一个循环根据这个来构建树
  3. 技巧:结点的组织方式是一个数组的时候,创建树的时候可以使用一个tmp[]数组先存放树的结点,便于操作

【测试数据】
在这里插入图片描述

#define INF 10000
typedef struct CSNode{
	VertexType data;
	struct CSNode *firstchild;
	struct CSNode *sibling;
	VRType childW; //孩子的边权重
	VRType siblingW; //兄弟的边权重
}CSNode, *CSTree;
VRType GetMSTByPrim(ALGraph G, int v0, CSTree *pT) {
	int MST[MAX_VERTEX_NUM]; //当前的生成树:MST[v]=1 表示 结点v在当前的生成树上
	VRType sum; //生成树的代价
	VRType lowCost[MAX_VERTEX_NUM]; //当前的生成树 到 结点v 的最小边的权重是lowCost[v]
	int lowNode[MAX_VERTEX_NUM];	//lowCost对应的顶点信息--> 到v的最小边是v->lowNode[v],权值为lowCost[v]
	
	VRType min; //当前的最小边
	int minNode; //最小边的另一个顶点
	
	CSNode *tmp[MAX_VERTEX_NUM];
	CSNode *csp;
	
	ArcNode *p;
	int i,j,parent;

	//初始化
	for (i=0; i<G.vernum; i++) {
		MST[i]=0;
		lowCost[i]=INF;
	}
	//处理v0结点
	MST[v0]=1; //将v0并入生成树
	tmp[v0] = (CSNode *)malloc(sizeof(CSNode)); if (!tmp[v0]) exit(OVERFLOW); //为生成树创建v0结点
	tmp[v0]->data = G.vers[v0].data; tmp[v0]->firstchild = tmp[v0]->sibling =NULL; //赋值
	for (p=G.vers[v0].firstarc; p; p=p->next) {
		//当前生成树到adjV结点的最小边是:v0到adjV的边
		lowCost[p->adjV]=p->weight; //记下权重
		lowNode[p->adjV]=v0;		//记下结点
	}

	sum=0;
	for (i=0; i<G.vernum-1; i++) { //还有n-1个结点要处理
		//选出最短的边
		min=INF;
		for (j=0; j<G.vernum; j++) {
			// 如果结点j还没有被并入生成树
			if (MST[j]==0 && lowCost[j]<min) {
				min=lowCost[j];
				minNode=j;
			}
		}
		
		//处理minNode
		MST[minNode]=1; //并入生成树
		sum += lowCost[minNode]; //生成树的权重
		tmp[minNode]=(CSNode *)malloc(sizeof(CSNode)); if (!tmp[minNode]) exit(OVERFLOW); //为生成树创建minNode结点
		tmp[minNode]->data = G.vers[minNode].data; tmp[minNode]->firstchild = tmp[minNode]->sibling=NULL; //赋值
		//连接边
		parent=lowNode[minNode]; //minNode的爸爸是lowNode[minNode],边的权重为lowCost[minNode]
		if (tmp[parent]->firstchild) { //第一个孩子存在
			for (csp=tmp[parent]->firstchild; csp->sibling; csp=csp->sibling) ; //找到最后一个孩子
			csp->sibling = tmp[minNode];	  //csp的兄弟是tmp[minNode]
			csp->siblingW = lowCost[minNode]; //边权重
		} else { //没有第一个孩子
			tmp[parent]->firstchild=tmp[minNode]; //孩子为tmp[minNode]
			tmp[parent]->childW=lowCost[minNode]; //边的权重为lowCost[minNode]
		}

		//更新minNode
		for (j=0; j<G.vernum; j++) {
			for (p=G.vers[minNode].firstarc; p; p=p->next) {
				if ( MST[p->adjV]==0 && p->weight<lowCost[p->adjV] ) {
					lowCost[p->adjV] = p->weight;
					lowNode[p->adjV] = minNode;
				}
			}
		}
	}

	*pT = tmp[0];
	return sum;
}

【完整代码】

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#ifndef BASE
#define BASE
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int bool;
#endif

#define VertexType char //点类型
#define VRType int //边类型
#define maxSize 100
void Visit(VertexType e) {
	printf("%c", e);
}

#define MAX_VERTEX_NUM 20
typedef enum{DG, UDG} GraphKind;
typedef struct ArcNode{
	int adjV; //边指向的顶点
	VRType weight; //权重
	struct ArcNode *next;
}ArcNode; //边
typedef struct VNode{
	VertexType data;
	ArcNode *firstarc;
}VNode, AdjList[MAX_VERTEX_NUM]; //顶点
typedef struct{
	GraphKind kind;
	int vernum,arcnum;
	AdjList vers; 
}ALGraph;

Status InitGraph_AL(ALGraph *pG) { //初始化
	int i;
	pG->arcnum = 0;
	pG->vernum = 0;
	for (i=0; i<MAX_VERTEX_NUM; ++i)
		pG->vers[i].firstarc = NULL; //VC++6.0中指针初始化为0xcccccccc
	return OK;
}
int LocateVex_AL(ALGraph G, VertexType e) { //定位值为e的元素下标
	int i;
	for (i=0; i<G.vernum; ++i) {
		if (G.vers[i].data == e) {
			return i;
		}
	}
	return -1;
}
// 创建无向图的邻接表--带权
Status CreateUDG_AL(ALGraph *pG) {
	//输入规则:顶点数目->弧的数目->各顶点的信息->各条弧的信息
	int i,a,b,w;
	char tmp[MAX_VERTEX_NUM];
	char h,t;
	ArcNode *p, *q;

	InitGraph_AL(pG); //VC++6.0中指针初始化为0xcccccccc,如果不将指针初始化为NULL,会出错
	//图的类型
	pG->kind = DG;
	//顶点数目
	scanf("%d", &i); if (i<0) return ERROR;
	pG->vernum = i;
	//弧的数目
	scanf("%d", &i); if (i<0) return ERROR;
	pG->arcnum = i;
	//各顶点信息
	scanf("%s", tmp);
	for (i=0; i<pG->vernum; ++i) pG->vers[i].data=tmp[i];
	//弧的信息
	for (i=0; i<pG->arcnum; ++i) {
		scanf("%s", tmp); //结点
		scanf("%d", &w); //权重
		h = tmp[0]; t = tmp[2]; //两个结点
		a = LocateVex_AL(*pG, h); //定位
		b = LocateVex_AL(*pG, t); //定位
		if (a<0 || b<0) return ERROR; //定位错误
		// a-->b
		p = (ArcNode *)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);
		p->adjV=b;p->next=NULL;p->weight=w;
		if (pG->vers[a].firstarc) { //已经有边了
			for (q = pG->vers[a].firstarc; q->next; q=q->next) ; //找到最后一条
			q->next = p;
		} else { //第一条边
			pG->vers[a].firstarc = p;
		}
		// b-->a
		p = (ArcNode *)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);
		p->adjV=a;p->next=NULL;p->weight=w;
		if (pG->vers[b].firstarc) { //已经有边了
			for (q = pG->vers[b].firstarc; q->next; q=q->next) ; //找到最后一条
			q->next = p;
		} else { //第一条边
			pG->vers[b].firstarc = p;
		}
	}
	return OK;
}

/*----------------------------------------------------------------
 |求最小生成树-Prim                                         |
 ----------------------------------------------------------------*/
#define INF 10000
typedef struct CSNode{
	VertexType data;
	struct CSNode *firstchild;
	struct CSNode *sibling;
	VRType childW; //孩子的边权重
	VRType siblingW; //兄弟的边权重
}CSNode, *CSTree;
VRType GetMSTByPrim(ALGraph G, int v0, CSTree *pT) {
	int MST[MAX_VERTEX_NUM]; //当前的生成树:MST[v]=1 表示 结点v在当前的生成树上
	VRType sum; //生成树的代价
	VRType lowCost[MAX_VERTEX_NUM]; //当前的生成树 到 结点v 的最小边的权重是lowCost[v]
	int lowNode[MAX_VERTEX_NUM];	//lowCost对应的顶点信息--> 到v的最小边是v->lowNode[v],权值为lowCost[v]
	
	VRType min; //当前的最小边
	int minNode; //最小边的另一个顶点
	
	CSNode *tmp[MAX_VERTEX_NUM];
	CSNode *csp;
	
	ArcNode *p;
	int i,j,parent;

	//初始化
	for (i=0; i<G.vernum; i++) {
		MST[i]=0;
		lowCost[i]=INF;
	}
	//处理v0结点
	MST[v0]=1; //将v0并入生成树
	tmp[v0] = (CSNode *)malloc(sizeof(CSNode)); if (!tmp[v0]) exit(OVERFLOW); //为生成树创建v0结点
	tmp[v0]->data = G.vers[v0].data; tmp[v0]->firstchild = tmp[v0]->sibling =NULL; //赋值
	for (p=G.vers[v0].firstarc; p; p=p->next) {
		//当前生成树到adjV结点的最小边是:v0到adjV的边
		lowCost[p->adjV]=p->weight; //记下权重
		lowNode[p->adjV]=v0;		//记下结点
	}

	sum=0;
	for (i=0; i<G.vernum-1; i++) { //还有n-1个结点要处理
		//选出最短的边
		min=INF;
		for (j=0; j<G.vernum; j++) {
			// 如果结点j还没有被并入生成树
			if (MST[j]==0 && lowCost[j]<min) {
				min=lowCost[j];
				minNode=j;
			}
		}
		
		//处理minNode
		MST[minNode]=1; //并入生成树
		sum += lowCost[minNode]; //生成树的权重
		tmp[minNode]=(CSNode *)malloc(sizeof(CSNode)); if (!tmp[minNode]) exit(OVERFLOW); //为生成树创建minNode结点
		tmp[minNode]->data = G.vers[minNode].data; tmp[minNode]->firstchild = tmp[minNode]->sibling=NULL; //赋值
		//连接边
		parent=lowNode[minNode]; //minNode的爸爸是lowNode[minNode],边的权重为lowCost[minNode]
		if (tmp[parent]->firstchild) { //第一个孩子存在
			for (csp=tmp[parent]->firstchild; csp->sibling; csp=csp->sibling) ; //找到最后一个孩子
			csp->sibling = tmp[minNode];	  //csp的兄弟是tmp[minNode]
			csp->siblingW = lowCost[minNode]; //边权重
		} else { //没有第一个孩子
			tmp[parent]->firstchild=tmp[minNode]; //孩子为tmp[minNode]
			tmp[parent]->childW=lowCost[minNode]; //边的权重为lowCost[minNode]
		}

		//更新minNode
		for (j=0; j<G.vernum; j++) {
			for (p=G.vers[minNode].firstarc; p; p=p->next) {
				if ( MST[p->adjV]==0 && p->weight<lowCost[p->adjV] ) {
					lowCost[p->adjV] = p->weight;
					lowNode[p->adjV] = minNode;
				}
			}
		}
	}

	*pT = tmp[0];
	return sum;
}

void TreeVisit(VertexType e) {
	printf("%c", e);
}
void PrintAsTree(CSTree T,int i) {
	int cnt;
	if (T) {
		//输出空格
		for (cnt=1; cnt<i; cnt++) printf(" ");
		//输出字符
		TreeVisit(T->data);
		printf("\n");

		PrintAsTree(T->firstchild, i+1);
		PrintAsTree(T->sibling, i);
	}
}


int main() {
/*
7
11
ABCDEFG
A,B
18
A,F
19
A,G
18
B,G
20
B,C
8
C,D
20
D,E
9
D,F
16
D,G
15
E,F
3
F,G
15
*/
	ALGraph G;
	int sum;
	CSTree T;

	G.kind = UDG;
	CreateUDG_AL(&G);

	sum = GetMSTByPrim(G, 0, &T);
	printf("最小生成的权重和为:%d\n", sum);
	PrintAsTree(T, 1);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/summer_dew/article/details/83024544