最小生成树Prim和Kruskal算法

本文介绍了一种使用邻接矩阵存储结构构建无向网的方法,并通过Prim和Kruskal算法来求解最小生成树。代码示例展示了如何创建图、构建邻接矩阵以及使用这两种算法进行计算。

采用邻接矩阵的存储结构构建无向网,然后用Prim和Kruskal算法求出最小生成树。

总代码:

#include <stdio.h>
#include <stdlib.h>
#define VRType int//在这里是权值类型
#define MAX_VERTEX_NUM 10//最大顶点个数
#define VertexType char //顶点类型
#define INFINITY 32767 //无穷大,不连通
//边的结构
typedef struct ArcCell{
	VRType adj;//权值
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
//图的结构
typedef struct {
	VertexType vexs[MAX_VERTEX_NUM];//顶点向量
	AdjMatrix arcs;//邻接矩阵
	int vexnum,arcnum;//顶点数,边数
}MGraph;
//辅助数组,记录从U到V-U的最小代价的边
typedef struct {
	VertexType adjvex;
	VRType lowcost;
}Closedge;
//////////
typedef struct edge{
	int begin;//起始顶点位置
	int end;//终点位置
	VRType weight;//权重
}Edge[100];

//函数声明
void CreateMGraph(MGraph &G);
void Prim(MGraph G,VertexType v);
void Kruskal(MGraph G);
int LocateVex(MGraph G,VertexType v);

int main(){
	int i=1;
	MGraph G;
	VertexType v;
	while(i){
		printf("第%d个图:\n",i++);
		CreateMGraph(G);
		//Prim算法
		printf("========Prim算法========:\n输入从哪个顶点开始构造:");
		fflush(stdin);
		scanf("%c",&v);
		Prim(G,v);
		//Kruskal算法
		printf("\n========Kruskal算法========:\n");
		Kruskal(G);
		system("pause");
	}
	return 0;
}
//函数实现
int minimum(Closedge *closedge,MGraph G){
	int i=0,j,k,min;
	while(!closedge[i].lowcost){//找到第一个权值不为零的
		i++;
	}
	min = closedge[i].lowcost;
	k = i;
	for(j=i+1;j<G.vexnum;j++){
		if(closedge[j].lowcost >0 && min >closedge[j].lowcost){
			min = closedge[j].lowcost;
			k = j;
		}
	}
	return k;
}
void Prim(MGraph G,VertexType v){
	int k = LocateVex(G,v),i=0,j=0;
	Closedge closedge[MAX_VERTEX_NUM];
	//辅助矩阵初始化
	for(i=0;i<G.vexnum;i++){
			closedge[i].adjvex = v;
			closedge[i].lowcost = G.arcs[k][i].adj;
//			printf("==%d\n",closedge[i].lowcost);
	}
	closedge[k].lowcost = 0;//把结点v加入集合U中
	for(i=1;i<G.vexnum;i++){
		k = minimum(closedge,G) ;//求出最小生成树的下一个节点,第k顶点
		printf("%c--->%c\n",closedge[k].adjvex,G.vexs[k]);
		closedge[k].lowcost = 0;//第k结点加入U
		//重新选择最小边
		for(j=0;j<G.vexnum;j++){
			if( G.arcs[k][j].adj < closedge[j].lowcost){
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arcs[k][j].adj;
			}
		}
	}
}
//打印边数组
void print(Edge *E,int n,MGraph G){
	for(int i=0;i<n;i++){
		printf("%c---%c---%d\n",G.vexs[E[i]->begin],G.vexs[E[i]->end],G.arcs[E[i]->begin][E[i]->end].adj);
	}
}
//按权值从小到大排序
int cmp(const void *a,const void *b){
	return ((struct edge*)a)->weight - ((struct edge*)b)->weight;
}
void Kruskal(MGraph G){
	Edge *E = (Edge*)malloc(sizeof(Edge)*G.arcnum*2) ;
	int i=0,j=0,k=0;
	for(i=0;i<G.vexnum;i++){
		for(j=0;j<G.vexnum;j++){
			if(G.arcs[i][j].adj != INFINITY){
				E[k]->begin = i;
				E[k]->end = j;
				E[k]->weight = G.arcs[i][j].adj;
				k++;
			}
		}
	}
	qsort(E,k,sizeof(E[0]),cmp);
	print(E,k,G);
	int *vset = (int *)malloc(sizeof(int)*G.vexnum);
	for (i=0;i<G.vexnum;i++){                                  //初始化辅助数组    
        vset[i]=i;  
    }  
    k=1;                                                   //生成的边数,最后要刚好为总边数   
    j=0;                                                   //E中的下标   
    while (k<G.vexnum){   
        int set1 = vset[E[j]->begin];  
        int set2 = vset[E[j]->end];                                  //得到两顶点属于的集合   
        if (set1!=set2){                                    //不在同一集合内的话,把边加入最小生成树   
			printf("%c--->%c weight = %d\n",G.vexs[E[j]->begin],G.vexs[E[j]->end],E[j]->weight);
            k++;  
            for (i=0;i<G.vexnum;i++)  
            {  
                if (vset[i]==set2)  
                {  
                    vset[i]=set1; //避免重复添加,像A-B添加后B-A又添加
                }  
            }             
        }  
        j++;  
    }  
	free(vset);
	free(E);
}
void CreateMGraph(MGraph &G){
	printf("输入顶点数,边数:");
	scanf("%d %d",&G.vexnum,&G.arcnum);
	int i=0,j=0;
	for(i=0;i<G.vexnum;i++){
		printf("输入第%d个顶点编号:",i+1);
		fflush(stdin);
		scanf("%c",&G.vexs[i]);
	}
	//初始化矩阵
	for(i=0;i<G.vexnum;i++){
		for(j=0;j<G.vexnum;j++){
			G.arcs[i][j].adj = INFINITY;
		}
	}
	char v1,v2;
	int w;
	for(int k=0;k<G.arcnum;k++){
		printf("输入顶点1,顶点2及其权值:");
		fflush(stdin);
		scanf("%c %c %d",&v1,&v2,&w);
		i = LocateVex(G,v1);
		j = LocateVex(G,v2);
		G.arcs[i][j].adj = w;
		G.arcs[j][i] = G.arcs[i][j];//对称边
//		printf("%d\n",G.arcs[j][i].adj);
	}
}
int LocateVex(MGraph G,VertexType v){
	for(int i=0;i<G.vexnum;i++){
		if(v == G.vexs[i]){
			return i;
		}
	}
	return -1;
}


运行结果:



Prim算法Kruskal算法都是用于求解最小生成树的经典算法Prim算法的基本思想是从一个点开始,每次选择一个与当前生成树距离最近的点加入生成树中,直到所有点都被加入生成树为止。具体实现时,可以使用一个优先队列来维护当前生成树与未加入生成树的点之间的距离,每次选择距离最小的点加入生成树中。 Kruskal算法的基本思想是从边开始,每次选择一条权值最小且不会形成环的边加入生成树中,直到生成树中包含所有点为止。具体实现时,可以使用并查集来判断是否形成环。 下面是Prim算法Kruskal算法的C语言代码实现Prim算法: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 int graph[MAX_VERTICES][MAX_VERTICES]; int visited[MAX_VERTICES]; int dist[MAX_VERTICES]; int prim(int n) { int i, j, u, min_dist, min_index, sum = 0; for (i = 0; i < n; i++) { visited[i] = 0; dist[i] = INT_MAX; } dist[0] = 0; for (i = 0; i < n; i++) { min_dist = INT_MAX; for (j = 0; j < n; j++) { if (!visited[j] && dist[j] < min_dist) { min_dist = dist[j]; min_index = j; } } u = min_index; visited[u] = 1; sum += dist[u]; for (j = 0; j < n; j++) { if (!visited[j] && graph[u][j] < dist[j]) { dist[j] = graph[u][j]; } } } return sum; } int main() { int n, m, i, j, u, v, w; scanf("%d%d", &n, &m); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { graph[i][j] = INT_MAX; } } for (i = 0; i < m; i++) { scanf("%d%d%d", &u, &v, &w); graph[u][v] = graph[v][u] = w; } printf("%d\n", prim(n)); return 0; } ``` Kruskal算法: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 #define MAX_EDGES 1000000 struct edge { int u, v, w; }; int parent[MAX_VERTICES]; struct edge edges[MAX_EDGES]; int cmp(const void *a, const void *b) { return ((struct edge *)a)->w - ((struct edge *)b)->w; } int find(int x) { if (parent[x] == x) { return x; } return parent[x] = find(parent[x]); } void union_set(int x, int y) { parent[find(x)] = find(y); } int kruskal(int n, int m) { int i, sum = 0; for (i = 0; i < n; i++) { parent[i] = i; } qsort(edges, m, sizeof(struct edge), cmp); for (i = 0; i < m; i++) { if (find(edges[i].u) != find(edges[i].v)) { union_set(edges[i].u, edges[i].v); sum += edges[i].w; } } return sum; } int main() { int n, m, i; scanf("%d%d", &n, &m); for (i = 0; i < m; i++) { scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w); } printf("%d\n", kruskal(n, m)); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值