【C语言】实现求最小生成树的Kruskal算法

    • 题目描述
    • 思想
    • 代码实现

题目描述

已知有权无向图G,利用克鲁斯卡尔算法求出该图的最小生成树。

输入格式:

第一行输入两个正整数n和m(空格间隔), 分别表示图G的顶点总数和边的总数。 第二行连续输入n个字母,分别表示n个顶点的信息。

第三行连续输入m条边的信息,每条边的输入格式为(v1,v2,w),表示一条关联顶点v1和v2的边,其权值为w。

输出格式:

按边上权值由小到大的顺序依次输出各个边。

输出边的时,若该边被选中,则在该边信息之后输出1,否则输出0。

例如: (v1,v2,w,1) 表示与顶点v1和v2相关联的边,权值为w,被选中。 (v1,v2,w,0)
表示与顶点v1和v2相关联的边,权值为w,未被选中。

输入样例:

6 10 ABCDEF
(A,B,3)(A,D,4)(A,F,10)(B,C,15)(B,D,1)(B,F,8)(C,D,17)(D,E,6)(D,F,13)(E,F,2)
结尾无空行

输出样例:

(B,D,1,1)(E,F,2,1)(A,B,3,1)(A,D,4,0)(D,E,6,1)(B,F,8,0)(A,F,10,0)(D,F,13,0)(B,C,15,1)(C,D,17,0)
结尾无空行

思想

我们可以把图里的每一个顶点看成是一个森林,每一个顶点就是一棵树。

算法思想:
kruskal算法即将每一条边按权值从小到大的顺序排序,选择权值最小的边加入到最小生成树里面
每次加入到最小生成树里面要判断是否会造成环形回路(关键)

如何判断?

判断两个顶点是否有相同的根节点

  • 如果有,就不进行操作

  • 如果没有,就将这两个顶点添加到生成树集合里面(parent[i]=j:i的双亲为j ---- 双亲表示法)

代码实现

#include
#include
#define MAX 32767
typedef struct Matrix//定义结构体 
{
	int arcs[20][20];//结构体大小 
	int poinum;//顶点的数目 
	int edenum;//边的数目 
	char vex[20];//顶点的信息 

}Matrix;

typedef struct edge //定义边的结构体 
{
	int begin;//纪录边的两个结点 
	int end;
	int weight;
	int isfind;//纪录是否被使用到 
}Edge;

Edge edge[20];//定义边集数组 

int Location(Matrix *M,char a)//查找函数 
{
	for(int i=0;i<M->poinum;i++){
		if(M->vex[i] == a)
			return i;
		
	}
	return 0;
}

//查找v所在树的根节点 
int find(int *parent, int v)
{
    while (parent[v] > 0)
    {
        v = parent[v];//寻找双亲结点直至根节点 
    }
    return v;
}

void Sort(Matrix *M,int k)
{
	//使用冒泡排序 
	Edge tmp;
	int i,j;
	for(i=0;i<k-1;i++)
	{
		for(j=i+1;j<k;j++)
		{
			if(edge[i].weight>edge[j].weight)
			{
				tmp = edge[i];
				edge[i] = edge[j];
				edge[j] = tmp;
			}
		}
	}
}


void kruskal(Matrix *M)
{
	int num;
	int parent[100] = {0};//纪录能否生成环路 - 数组纪录双亲结点的位置 
	int i,j,k=0;
	int v1,v2;
	//将邻接矩阵转换为边集数组 
	for(i=0;i<M->poinum-1;i++)
	{
		for(j=i+1;j<M->poinum;j++)
		{
			if(M->arcs[i][j]!=MAX)
			{
				edge[k].begin = i;//将行下标放到begin里面 
				edge[k].end = j;//将列下标放到end里面 
				edge[k].weight = M->arcs[i][j];//权值赋 
				edge[k].isfind = 0;//判断边是否被使用的变量初始化为0 
				k++;//边集数组的下标 
			}
		}
	}
	Sort(M,k);//按权值升序排序 
	
	for(i=0;i<M->edenum;i++)
	{
		v1 = find(parent,edge[i].begin);//查找begin的根节点 
		v2 = find(parent,edge[i].end);//查找end的根节点 
		
		if(v1!=v2)//判断根节点是否相同 
		{
			parent[v1] = v2;//v1的双亲结点为v2 - 标志着v2顶点加入到生成树集合里面 
			edge[i].isfind = 1;//改变是否被使用 - 改为已使用 
			num++;//变量num纪录加入生成树边的数目 
		}
		//优化:提前退出 
		printf("(%c,%c,%d,%d)",M->vex[edge[i].begin], M->vex[edge[i].end], edge[i].weight, edge[i].isfind);
		if(num==M->poinum-1)return;//如果加入生成树的边数等于顶点数目-1,可以退出不再进行判断 
	}
}

int main()
{
	int i,j,k,weight;
	Matrix *M = (Matrix*)malloc(sizeof(Matrix));//申请邻接矩阵空间 
	char vex1,vex2;
	scanf("%d%d",&M->poinum,&M->edenum);
	getchar();
	for(i=0;i<M->poinum;i++){
		for(j=0;j<M->poinum;j++){
			M->arcs[i][j] = MAX;//邻接矩阵初始化 
		}
	} 
	for(i=0;i<M->poinum;i++)
	{
		scanf("%c",&M->vex[i]);
	}
	getchar();
	
	for(i=0;i<M->edenum;i++)
	{
		scanf("(%c,%c,%d)",&vex1,&vex2,&weight);
		j = Location(M,vex1);//查找所在行下标 
		k = Location(M,vex2);//查找所在列下标 
		M->arcs[j][k] = weight;//保存权值 
		M->arcs[k][j] = M->arcs[j][k];//无向图 - 对称的 
	} 
	
	kruskal(M); 
}

你可能感兴趣的:(C语言笔记,链表,数据结构)