最小生成树之二:Kruskal算法

Kruskal算法(并查集+QuickSort)

概述

kruskal算法主要是根据贪心算法并查集算法结合实现的。

贪心策略

在所有边中选择权值最小的边。

条件

所选择的边所在的点不能是树中的结点(不能形成回路,根据并查集实现)

思路

每次选择最小的边,直到所有的点连成树。

步骤

整个代码由三大部分组成:
1、初始化
2、排序
3、选择加入

Kruskal算法与 Prim算法的区别:

贪心策略不同,Kruskal算法是在所有边中选择最小的边,
Prim算法是在与已经连接的结点相邻的结点中选择最小的边。
Prim算法适合稠密图,Kruskal算法适合稀疏图

Kruskal算法与 并查集算法的区别:

Kruskal算法形成的是最小生成树,而并查集只是判断结点是否在同一颗树,并且将其连成树。

代码中关键部分

1、如何存储边的关系。我采用的是结构体的方式,存放每组边的权值以及两个结点。
2、如果对各组边进行从小到大的排序。采用的是在快速排序的基础上对每组边进行交换。
3、如何与并查集结合使用。最开始我以为对边进行排序会影响到并查集的使用,但是后来我发现两者没有关系,并查集只需要对已排好序的边进行判断将要相连的结点是否在同一个树中。

代码用例

#include <iostream>
#include <algorithm>
using namespace std;
const int inf = 88888;
int n,m; 
int a,b,c;
int map[100][100];	//图结构 
int dis[100];
int id[100];		//并查集 

//边关系 
typedef struct 
{
	int weight;	//权值
	int begin;	//开始结点
	int end;	//结束结点 
}Element1;

//初始化 
void init(Element1 Element[])
{
	//初始化图
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n;j++)
			if(i == j) map[i][j] = 0;
			else map[i][j] = map[j][i] = inf;
			
	//给各边关系赋值 
	for(int i = 0; i < m; i++)
	{
		cin >> a >> b >> c;
		map[a][b] = map[b][a] = c;	
		//初始化结构体元素 
		Element[i].weight = c;
		Element[i].begin = a;
		Element[i].end = b;
	}
	
	for(int i = 0; i < n; i++)
		id[i] = i;
}
//快速排序
int partition(Element1 Element[],int i,int j)
{
	
	while(i < j)
	{
		while(i < j && Element[i].weight <= Element[j].weight) j--;
		if(i < j )
		{
			swap(Element[i].weight,Element[j].weight);
			swap(Element[i].begin,Element[j].begin);
			swap(Element[i].end,Element[j].end);
		}
		while(i < j && Element[i].weight <= Element[j].weight) i++;
		if(i < j )
		{
			swap(Element[i].weight,Element[j].weight);
			swap(Element[i].begin,Element[j].begin);
			swap(Element[i].end,Element[j].end);
		}
	}
	return i;
}

void quick_sort(Element1 Element[],int i,int j)
{
	if(i < j)
	{
		int priovt = partition(Element,i,j);
		quick_sort(Element,i,priovt-1);
		quick_sort(Element,priovt+1,j);
	}
}
 
 //并查集
 //查找根结点
 int find(int p)
 {
 	while(p != id[p])
 	{
 		p = id[p];
	 }
	 return p; 
  } 
//Kruskal算法 
void Kruskal(Element1 Element[])
{
	int begin = 0; 		//边开始结点 
	int end = 0; 		//边结束结点 
	//遍历所有的边 
	for(int i = 0; i < m; i++)
	{
		//获取最小边的两个结点,判断是否在同一个树中 
		begin = Element[i].begin;
		end = Element[i].end;
		//并查集中的并操作 
		if(find(begin) != find(end))
		{
			//一定要将根结点进行连接!!! 
			id[find(begin)] = find(end); 
			cout << "连接:" << begin << "-----" << end << endl;
		}
	}
 } 

//输出图关系 
 void getMap()
{
		 
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < n; j++)
		{
			cout << map[i][j] << "   "; 
		}
		cout << endl;
	}
	cout << endl;
}

int main(int argc, char** argv) {
	while(cin >> n >> m)
	{
		Element1 Element[100]; 		
		init(Element);
	//	getMap();
		quick_sort(Element,0,m-1); 
		cout << " 排序后:";
		for(int i = 0; i < m; i++)
		{
			cout << Element[i].weight << "  " 
			<< Element[i].begin << "  " << Element[i].end << endl;
		}
		Kruskal(Element);
	}
/*  测试用例
6 9
0 1 34
0 2 46
0 5 19
1 4 12
2 5 25
2 3 17
3 5 25
3 4 38 
4 5 26
*/	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值