欢迎光临散文网 会员登陆 & 注册

最小生成树-克鲁斯卡尔算法

2023-01-14 10:01 作者:就叫大嘴吧  | 我要投稿


#include <stdio.h>
#include <stdlib.h>
#define VertexMax 20 //最大顶点数为20

typedef char VertexType;

// 采用 边集数组 存储图中的边 
typedef struct{
	VertexType begin;
	VertexType end;
	// 边上的权值 
	int weight;
}Edge;//边集数组 edge[]的单元 

typedef struct{
	//顶点数组 
	VertexType Vertex[VertexMax];
	//边集数组 
	Edge edge[VertexMax];
	//顶点数 
	int vexnum;
	//边数 
	int edgenum;
}EdgeGraph;

void CreateEdgeGraph(EdgeGraph *E){
	int i;
	// 1.输入顶点数和边数
	printf("请输入顶点数和边数:\n");
	printf("顶点数 n = "); 
	scanf("%d", &E->vexnum);
	printf("边  数 e = "); 
	scanf("%d", &E->edgenum);
	printf("\n"); 
	
	// 2.输入顶点元素 
	printf("输入顶点(无需空格隔开):"); 
	scanf("%s", &E->Vertex);
	printf("\n\n");
	
	printf("输入边信息和权值(如:A B 15):\n");
	for(i = 0; i < E->edgenum; i++){
		printf("请输入第 %d 边的信息:", i+1);
		scanf(" %c %c %d", &E->edge[i].begin, &E->edge[i].end, &E->edge[i].weight);
	}	
}

void print(EdgeGraph *E){
	int i;
	
	printf("-----------------------------------\n"); 
	printf(" 顶点数组Vertex:");
	for(i = 0; i < E->vexnum; i++){
		printf("%c ", E->Vertex[i]);
	}
	printf("\n\n");
	
	printf(" 边集数组edge:\n\n");
	printf("\t\tBegin	End	Weight\n");
	for(i = 0; i < E->edgenum; i++){
		printf("\tedge[%d]	%c	%c	%d\n", i, E->edge[i].begin, E->edge[i].end,E->edge[i].weight);
	}
	printf("-----------------------------------\n");
}

int partition(EdgeGraph *E, int low, int high){
	Edge pivot = E->edge[low];

	while(low < high){
		while(low < high && pivot.weight <= E->edge[high].weight){
			high--;
		}
		E->edge[low] = E->edge[high];
		
		while(low < high && E->edge[low].weight <= pivot.weight){
			low++;
		}
		E->edge[high] = E->edge[low];
	}
	E->edge[low] = pivot;
	// 返回 中间位置 
	return low;
}

// 将 图中的边 从小到大进行排序 (快速排序)
void quickSort(EdgeGraph *E, int low, int high){
	// 递归出口条件 
	if(low < high){
		// 划分 
		int pos = partition(E, low, high);
	
		quickSort(E, low, pos - 1);
		quickSort(E, pos + 1, high);
	}
} 

//查找元素v在一维数组 Vertex[] 中的下标,并返回下标 
int LocateVertex(EdgeGraph *E, VertexType v){
	int i;
	for(i = 0; i < E->vexnum; i++){
		if(v == E->Vertex[i]){
			return i; 
		} 
	}
	printf("No Such Vertex!\n");
	return -1;
}

// 参数 v:结点在Vertex数组中的下标 
int FindRoot(int v,int parent[]){
	//parent=-1表示没有双亲,即没有根节点 
	while(parent[v] != -1){
		//逐代查找根节点 
		v = parent[v];
	}
	//将找到的根节点返回,若没有根节点,返回自身 
	return v;
}

void outputMST(EdgeGraph *E, int i){
	printf("%c — %c, w = %d\n", E->edge[i].begin, E->edge[i].end, E->edge[i].weight);//输出此边 
}

// 最小生成树-克鲁斯卡尔算法算法 
void MiniSpanTree_Kruskal(EdgeGraph *E){
	int i;
	//生成边计数器,当 num = 顶点数-1 时,最小生成树生成完毕 
	int num;
	int root1, root2; 
	int LocateV1, LocateV2; 
	//用于查找顶点的双亲,判断两个顶点间是否有共同的双亲,来判断生成树是否会成环 
	int parent[VertexMax];
	
	//1.按权值从小到大排序 
	printf("排序前的边集数组:\n");
	print(E); 
	quickSort(E, 0, E->edgenum -1);
	printf("排序后的边集数组:\n");
	print(E);
	
	//2.初始化parent数组 
	for(i = 0; i < E->vexnum; i++){
		// 视 图中所有顶点 都是 一棵只有根结点的树 
		parent[i] = -1;
	}
	
	printf("\n 最小生成树(Kruskal):\n\n");
	//3. for循环,遍历 edge边数组 
	for(num = 0, i = 0; i < E->edgenum; i++){
		LocateV1 = LocateVertex(E, E->edge[i].begin);
		LocateV2 = LocateVertex(E, E->edge[i].end);
		// 找到 所在生成树的根结点 
		root1 = FindRoot(LocateV1, parent);
		root2 = FindRoot(LocateV2, parent);
	
		// 若不会成环,则在最小生成树中构建此边
		// 根结点不相等 即 在不同的连通分量中 
		if(root1 != root2){
			// 输出该边 
			outputMST(E, i);
			//合并生成树,将 root1 作为 根结点,即 root2 的双亲为 root1 
			parent[root2] = root1;
			// 最小生成树边数 + 1 
			num++;
			
			//若num=顶点数-1,代表树生成完毕,提前结束 
			if(num == E->vexnum-1){
				return;
			}
		} 
	}
}

int main(){
	EdgeGraph E;
	CreateEdgeGraph(&E);
	MiniSpanTree_Kruskal(&E);
	
	return 0;
}
测试样例


最小生成树-克鲁斯卡尔算法的评论 (共 条)

分享到微博请遵守国家法律