KrusKal求最小生成树

这个算法的主要难点是:怎么避免连通图成环,可以用并查集算法

参考:http://blog.youkuaiyun.com/dellaserss/article/details/7724401/

图例:


代码:

#include<stdio.h>
#include<stdlib.h>
/*主要采用并查集来判断是否成环,加入了路径压缩*/
#include <malloc.h>
#include <string.h>
#define MAX 20
#define nLENGTH(a)  (sizeof(a)/sizeof(a[0]))
#define eLENGTH(a) ( sizeof(a) / sizeof(char) )/ ( sizeof(a[0]) / sizeof(char) )
//邻接矩阵
typedef struct _graph
{
    char vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
}Graph, *PGraph; 
// 边的结构体
typedef struct _EdgeData
{
    char start; // 边的起点
    char end;   // 边的终点
    int weight; // 边的权重
}EData;
//指向节点的位置
int point_node(PGraph g,char c)
{

	for(int i=0;i<g->vexnum;i++)
	{
		if(g->vexs[i]==c)
		{
			return i;
		}
	}
	return -1;
}
//对边按权值大小排序
void Sort_edg(EData edg[],int e)
{
	int i,j;

    for (i=0; i<e; i++)
    {
        for (j=i+1; j<e; j++)
        {
            if (edg[i].weight > edg[j].weight)
            {
                // 交换"第i条边"和"第j条边"
                EData tmp = edg[i];
                edg[i] = edg[j];
                edg[j] = tmp;
            }
        }
    }	
	for(j=0;j<e;j++)
	{
		printf("%c--%c\t%d",edg[j].start,edg[j].end,edg[j].weight);
		printf("\n");
	}

	

}
/*并查集的主要思路以下:*/
//-------------------------------------------------------------------------------
//找到根顶点       
int FindRoot(int a[],int p)
{
	int r=p;
	while(a[r]!=r)
		r=a[r];
	//路径压缩
	int x=p,j;
	while(x!=r)
	{
		j=a[x]; //j 保存 p 的父顶点
		a[j]=r; //把 终端顶点 r 赋给 p的父顶点
		x=j;   //把j值赋给x,不断循环,把父级的父级 r 一次一次赋给 x ,直到x与 r相等
	}
	return r;

}
//--------------------------------------------------------------------------------
void KrusKalTree(int b[][3],char a[],int L,int e)
{
	int rand=0;
	PGraph g; //矩阵
	EData edg[MAX];//保存边数组
	int ch[MAX]={0,1,2,3,4,5,6};//并查集用
    EData rets[MAX]; //用来存放选择好的边
	g=(PGraph)malloc(sizeof(Graph));
	//memset()第一个参数 是地址,第二个参数是开辟空间的初始值,第三个参数是开辟空间的大小
	printf("顶点个数:\n");//顶点数
	g->vexnum=L;
	printf("%d\n",g->vexnum);
	printf("边个数:\n");//边数
	g->edgnum=e;
	printf("%d\n",g->edgnum);
	//初始化顶点
	for(int j=0;j<g->vexnum;j++)
	{
		g->vexs[j]=a[j];
	}
	//得到边的数组
	for(int i=0;i<e;i++)
	{
		edg[i].start=char(b[i][0]);
		edg[i].end=char(b[i][1]);
		edg[i].weight=b[i][2];
	}
	//对边进行排序
	Sort_edg(edg,e);
	//进行并查集求解
	for(int k=0; k< e ; k++)
	{
		int p1,p2,m,n;
		p1=point_node(g,edg[k].start);
		p2=point_node(g,edg[k].end);
		
		//找他们的根顶点
		m=FindRoot(ch,p1);
		n=FindRoot(ch,p2);
		if(m!=n)
		{
			ch[m]=n;
			rets[rand++]=edg[k];
		}
	}
	printf("得到的最小生成树:\n");
	//打印已经得到的最小生成树的边
	for(j=0;j<rand;j++)
	{
		printf("%c--%c\t%d",rets[j].start,rets[j].end,rets[j].weight);
		printf("\n");

	}
	//通过循环,可以看出,压缩路径起了作用,不用通过循环即可,找到终端顶点
	for(j=0;j<rand;j++)
	{
		printf("%d\t",ch[i]);

	}
}

//测试
int main()
{
	int i,j;
	PGraph gp;
	//测试用例

	char a[]={'A', 'B', 'C', 'D', 'E', 'F', 'G'};
	int b[][3]={
        {'A', 'B',12}, 
        {'A', 'F',16}, 
        {'A', 'G',14}, 
        {'B', 'F',7}, 
        {'B', 'C',10}, 
        {'C', 'F',6}, 
        {'C', 'E',5},
		{'C', 'D',3},
		{'D', 'E',4},
		{'E', 'F',2},
		{'E', 'G',8},
		{'F', 'G',9}}; 

	//测试用例

	int n=nLENGTH(a);
	int e=eLENGTH(b);
	KrusKalTree(b,a,n,e);

	return 0;

}


### Kruskal算法简介 Kruskal算法是一种用于寻找加权无向最小生成树的有效算法。此算法的核心在于按权重升序选择边,并利用并查集检测所选边是否会形成环路,从而逐步构建最小生成树[^1]。 ### 算法流程描述 对于给定的一个连通G=(V,E),其中V表示顶点集合而E代表边的集合: - 将所有的边按照其权重从小到大排序; - 初始化一个空的结果列表用来保存最终形成的最小生成树MST; - 对于每一个未处理过的边(u,v,w),如果加入这条边不会造成循环,则将其添加至MST中;这里w指的是u和v之间的距离或成本; - 当所有可能被考虑进去而不引起闭环现象的边都已经被考察完毕之后停止操作,此时获得的就是原的一棵最小生成树[^2]。 ### C++代码实例展示 下面给出一段基于上述原理编写的C++版本Kruskal算法实现: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Edge { int src, dest, weight; }; class DisjointSet { private: vector<int> parent, rank; public: DisjointSet(int n); void Union(int x, int y); int Find(int x); }; DisjointSet::DisjointSet(int size):parent(size),rank(size){ for (int i=0;i<size;++i){ parent[i]=i; rank[i]=0; } } void DisjointSet::Union(int x,int y){ int rootX = Find(x); int rootY = Find(y); if(rootX != rootY){ if(rank[rootX]>rank[rootY]){ parent[rootY]=rootX; }else{ parent[rootX]=rootY; if(rank[rootX]==rank[rootY]) ++rank[rootY]; } } } int DisjointSet::Find(int x){ if(parent[x]!=x){ parent[x]=Find(parent[x]); } return parent[x]; } bool compare(Edge a, Edge b) {return a.weight<b.weight;} // Function to construct MST using Kruskal's algorithm pair<vector<Edge>, int> kruskalMST(vector<Edge>& edges, int V) { sort(edges.begin(),edges.end(),compare); // Sort all the edges based on their weights DisjointSet ds(V); vector<Edge> mstEdges; int totalWeight = 0; for(auto& edge : edges){ int set_u = ds.Find(edge.src); int set_v = ds.Find(edge.dest); if(set_u!=set_v){ // If including this edge does not form cycle then include it in result and union two sets of vertices. mstEdges.push_back({edge}); ds.Union(set_u,set_v); totalWeight+=edge.weight; if(mstEdges.size()==V-1)// Once we have included V-1 edges break out from loop as now our graph is fully connected with minimum cost possible. break; } } return make_pair(mstEdges,totalWeight); } ``` 这段程序实现了完整的Kruskal算法逻辑,能够接收一组带权边作为输入参数,并返回由这些边组成的最小生成树以及对应的总重量[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值