易懂的:图的最小生成树——Kruskal

文章目录

  • 图的最小生成树——Kruskal
      • 算法核心思想
      • 前序
      • 细讲
          • 头文件&&全局变量
          • 快速排序
          • 并查集
          • Kruskal算法核心(在主函数中)
      • 完整代码

图的最小生成树——Kruskal

算法核心思想

1. 去除所有的边形成点集

2.在不形成环路的前提下(因为树 无环 的 连通图):
   取权最小的边

前序

 图的最小生成树——Kruskal 
data	2020. 8. 10
=============
Kruskal算法的主要思想
	对边权快速排序
	并查集防止顶点重复进入集合 
	抽取 n-1 个边 (即可链接 n 个点)
	 
=============
	将树合并
 	适合稀疏图
	 
==============
图——储存方式:
	结构体 
==============
输入——:

输入顶点个数n,边数m:
6 9

for读取各个结点之间的边 
2 4 11
3 5 13
4 6 3

5 6 4
2 3 6
4 5 7

1 2 1
3 4 9
1 3 2 

细讲

需要用到,排序算法和并查集算法

头文件&&全局变量
#include

struct edge
{
	int u;
	int v;
	int w;
};

struct edge e[10]; 

int n,m; 

int f[7]={0},sum=0,count=0; 
快速排序

// 用任何排序算法都可以,只要对边进行排序就好。
// 根据排好序的边,要进行取最小边进行建树。

void quicksort(int left,int right)
{
	int i,j;
	struct edge t;
	
	if(left > right)
		return ;
		
	i = left;
	j = right;
	
	while(i != j)
	{
		//一定从右边开始找
		while(e[j].w >= e[left].w && i < j)
			j--;
		 //再从左边开始找
		while(e[i].w <= e[left].w && i < j)
		 	i++;
		
		//交换
		if(i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		} 
	}
	
	//最终将基准数归为,将 left 和 i 交换 
	t = e[left];
	e[left] = e[i];
	e[i] = t;
		
	quicksort(left,i-1); //左 边排序 
	quicksort(i+1,right); //右 边排序 
	
	return ; 
} 
并查集

将被边连接的点放入同一个集合中。

int getf(int v)
{
	if(f[v]==v)
		return v;
	else
	{
		f[v] = getf(f[v]);
		return f[v];
	}
} 

int merge(int v, int u)
{
	int t1,t2;
	//递归找爹 
	t1 = getf(v);
	t2 = getf(u);
	
	if(t2 != t1)
	{
		printf("%d  %d\n",v,u); 
		f[t2] = t1;
		return 1; 
	}
	
	return 0;
}

Kruskal算法核心(在主函数中)

从最小权的边开始连接点:

判断边的两个顶点是否已经在同一个集合中
(如果在,这两个顶点组成的边不可以要,防止 形成环,而不是树)
(如果属于两个集合,那么可以要这条边)
	//Kruskal算法核心部分
	for(i=1; i<=m ;i++)
	{
		//两个顶点已经联通(同一个集合) 
		if( merge(e[i].u ,e[i].v )  )
		{
			count++;
			sum = sum +e[i].w; 
		} 
		
		if(count==n-1)
			break;
	} 

完整代码

/* 图的最小生成树——Kruskal 
data	2020. 8. 10
=============
Kruskal算法的主要思想
	对边权快速排序
	并查集防止顶点重复进入集合 
	抽取 n-1 个边 (即可链接 n 个点)
	 
=============
	将树合并
 	适合稀疏图
	 
==============
图——储存方式:
	结构体 
==============
输入——:

输入顶点个数n,边数m:
6 9

for读取各个结点之间的边 
2 4 11
3 5 13
4 6 3

5 6 4
2 3 6
4 5 7

1 2 1
3 4 9
1 3 2 

===========
过程——
解释:
	
========== 

*/

#include

struct edge
{
	int u;
	int v;
	int w;
};

struct edge e[10];

int n,m;

int f[7]={0},sum=0,count=0; 

void quicksort(int left,int right)
{
	int i,j;
	struct edge t;
	
	if(left > right)
		return ;
		
	i = left;
	j = right;
	
	while(i != j)
	{
		//一定从右边开始找
		while(e[j].w >= e[left].w && i < j)
			j--;
		 //再从左边开始找
		while(e[i].w <= e[left].w && i < j)
		 	i++;
		
		//交换
		if(i < j)
		{
			t = e[i];
			e[i] = e[j];
			e[j] = t;
		} 
	}
	
	//最终将基准数归为,将 left 和 i 交换 
	t = e[left];
	e[left] = e[i];
	e[i] = t;
		
	quicksort(left,i-1); //左 边排序 
	quicksort(i+1,right); //右 边排序 
	
	return ; 
} 

int getf(int v)
{
	if(f[v]==v)
		return v;
	else
	{
		f[v] = getf(f[v]);
		return f[v];
	}
} 

int merge(int v, int u)
{
	int t1,t2;
	//递归找爹 
	t1 = getf(v);
	t2 = getf(u);
	
	if(t2 != t1)
	{
		printf("%d  %d\n",v,u); 
		f[t2] = t1;
		return 1; 
	}
	
	return 0;
}



int main()
{
	int i;
	
	printf("图的最小生成树——Kruskal:\n\n输入顶点个数n,边数m:\n ");
	scanf("%d %d",&n, &m); 
	
	printf("\nfor输入边:\n"); 
	for(i=1; i<=m ;i++)
		scanf("%d %d %d",&e[i].u ,&e[i].v ,&e[i].w );

	
	//按照权值,从小到大对边进行快速排序
	quicksort(1,m); 
	printf("\n快速排序后:\n"); 
	for(i=1; i<=m ;i++)
		printf("%d %d %d\n", e[i].u , e[i].v , e[i].w );
	
	//并查集初始化
	for(i=1; i<=n ;i++)
		f[i]=i;
	
	printf("\n选取的边有:\n"); 
	//Kruskal算法核心部分
	for(i=1; i<=m ;i++)
	{
		//两个顶点已经联通(同一个集合) 
		if( merge(e[i].u ,e[i].v )  )
		{
			count++;
			sum = sum +e[i].w; 
		} 
		
		if(count==n-1)
			break;
	} 
	
	printf("\n\n图的最小生成树——Kruskal :\n总花费为 %d\n",sum);
	
	return 0; 
	 
}
 

你可能感兴趣的:(数据结构,C语言,算法,算法,数据结构)