数据结构:最小生成树--Kruskal算法

数据结构:最小生成树--Kruskal算法

标签: Kruskal算法并查集kruskal数据结构
2308人阅读 评论(1) 收藏 举报
分类:

                           Kruskal算法

Kruskal算法

    求解最小生成树的另一种常见算法是Kruskal算法,它比Prim算法更直观。从直观上看,Kruskal算法的做法是:每次都从剩余边中选取权值最小的,当然,这条边不能使已有的边产生回路。

手动求解会发现Kruskal算法异常简单,下面是一个例子


先对边的权值排个序:1(0,1) 2(2,6) 4(1,3) 6(1,2) 8(3,6) 10(5,6) 12(3,5) 15(4,5) 20(0,1)

首选边1(0,1)、2(2,6)、4(1,3)、6(1,2),此时的图是这样



显然,若选取边8(3,6)会出现环,则必须抛弃8(3,6),选择下一条10(5,6)没有问题,此时图变成这样



显然,12(3,5)同样不可取,选取15(4,5),边数已达到要求,算法结束。最终的图是这样的



算法逻辑人很容易理解,但用代码判断当前边是否会引起环的出现,则很棘手。

算法说明

    为了判断环的出现,我们换个角度来理解Kruskal算法的做法:初始时,把图中的n个顶点看成是独立的n个连通分量,从树的角度看,也是n个根节点。我们选边的标准是这样的:若边上的两个顶点从属于两个不同的连通分量,则此边可取,否则考察下一条权值最小的边。

    于是问题又来了,如何判断两个顶点是否属于同一个连通分量呢?这个可以参照并查集的做法解决。它的思路是:如果两个顶点的根节点是一样的,则显然是属于同一个连通分量。这也同样暗示着:在加入新边时,要更新父节点。具体细节看代码:

代码

  1. #include<iostream>   
  2. #include<algorithm>  
  3. using namespace std;  
  4. #define MAXWEIGHT 100  
  5. /* 
  6. 全局变量 
  7. numV顶点数 
  8. numE边数 
  9. */  
  10. int numV, numE;  
  11. //边  
  12. typedef struct edge_tag  
  13. {  
  14.     int tail;  
  15.     int head;  
  16.     int weight;  
  17. }Edge;  
  18. //检测边  
  19. bool checkEdge(int tail, int head, int weight)  
  20. {  
  21.     if (tail == head  
  22.         || tail < 0 || tail >= numV  
  23.         || head < 0 || head >= numV  
  24.         || weight <= 0 || weight >= MAXWEIGHT)  
  25.         return false;  
  26.     return true;  
  27. }  
  28. //输入边  
  29. void inputEdge(Edge *edge, int numE)  
  30. {  
  31.     int i, tail, head, weight;  
  32.     cout << "输入边的起点、终点和权值" << endl;  
  33.     i = 0;  
  34.     while (i < numE)  
  35.     {  
  36.         cin >> tail >> head >> weight;  
  37.         while (!checkEdge(tail, head, weight))  
  38.         {  
  39.             cout << "输入错误!重新输入" << endl;  
  40.             cin >> tail >> head >> weight;  
  41.         }  
  42.         edge[i].tail = tail;  
  43.         edge[i].head = head;  
  44.         edge[i].weight = weight;  
  45.         i++;  
  46.     }  
  47. }  
  48. int cmp(const void *edge1, const void *edge2)  
  49. {  
  50.     return ((Edge*)edge1)->weight - ((Edge*)edge2)->weight;  
  51. }  
  52.   
  53. //并查集的常见操作  
  54. /* 
  55. 压缩查找 
  56. 查找child的根节点 
  57. */  
  58. int Find(int child, int *parent)  
  59. {  
  60.     if (parent[child] == child)  
  61.         return child;  
  62.     parent[child] = Find(parent[child], parent); //压缩路径  
  63.     return parent[child];  
  64. }  
  65. //合并  
  66. bool Union(Edge *e, int *parent, int *childs)  
  67. {  
  68.     //处于同一个棵树中,则不能合并,否则会出现环  
  69.     int root1, root2;  
  70.     root1 = Find(e->tail, parent);  
  71.     root2 = Find(e->head, parent);  
  72.     if (root1 != root2)  
  73.     {  
  74.         //把小树合并到大树根下  
  75.         if (childs[root1] > childs[root2])  
  76.         {  
  77.             parent[root2] = root1;  
  78.             childs[root1] += childs[root2] + 1;  
  79.         }  
  80.         else  
  81.         {  
  82.             parent[root1] = root2;  
  83.             childs[root2] += childs[root2] + 1;  
  84.         }  
  85.         return true;  
  86.     }  
  87.     return false;  
  88. }  
  89. /* 
  90. Kruskal算法 
  91. 求最小生成树 
  92. */  
  93. void Kruskal(int numV, int numE)  
  94. {  
  95.     //边的集合  
  96.     Edge *edge = new Edge[numE];  
  97.     inputEdge(edge, numE);  
  98.     /* 
  99.     parent[i]是顶点i的父顶点 
  100.     childs[i]是顶点i的孩子数 
  101.     child的复数是children,这里的childs是杜撰的 
  102.     */  
  103.     int *parent = new int[numV];  
  104.     int *childs = new int[numV];  
  105.     //初始化  
  106.     for (int i = 0; i < numV; i++)  
  107.     {  
  108.         /* 
  109.         初始时,每一个顶点都是根,且无孩子 
  110.         把每个顶点的的父节点设置为自身下标,也是标明类别 
  111.         */  
  112.         parent[i] = i;  
  113.         childs[i] = 0;  
  114.     }  
  115.     //对边按权值进行从小到大排序  
  116.     qsort(edge, numE, sizeof(Edge), cmp);  
  117.     int i, count;  
  118.     i = count = 0;  
  119.     cout << "最小生成树的边..." << endl;  
  120.     while (i < numE)  
  121.     {  
  122.         if (Union(&edge[i], parent, childs))  
  123.         {  
  124.             cout << edge[i].tail << "---" << edge[i].head << endl;  
  125.             count++;  
  126.         }  
  127.         if (count == numV - 1)  //边数达到要求  
  128.             break;  
  129.         i++;  
  130.     }  
  131.     if (count != numV - 1)  
  132.         cout << "此图为非连通图!无法构成最小生成树。" << endl;  
  133.     delete[]edge;  
  134.     delete[]parent;  
  135.     delete[]childs;  
  136. }  
  137. //检测顶点数和边数  
  138. bool checkVE(int numV, int numE)  
  139. {  
  140.     if (numV <= 0 || numE <= 0 || numE > numV*(numV - 1) / 2)  
  141.         return false;  
  142.     return true;  
  143. }  
  144. int main()  
  145. {  
  146.     cout << "******Kruskal***by David***" << endl;  
  147.     cout << "输入顶点数、边数 ";  
  148.     cin >> numV >> numE;  
  149.     while (!checkVE(numV, numE))  
  150.     {  
  151.         cout << "输入数据有问题!重新输入 ";  
  152.         cin >> numV >> numE;  
  153.     }  
  154.     cout << endl << "Kruskal..." << endl;  
  155.     Kruskal(numV, numE);  
  156.     system("pause");  
  157.     return 0;  
  158. }  
#include<iostream> 
#include<algorithm>
using namespace std;
#define MAXWEIGHT 100
/*
全局变量
numV顶点数
numE边数
*/
int numV, numE;
//边
typedef struct edge_tag
{
	int tail;
	int head;
	int weight;
}Edge;
//检测边
bool checkEdge(int tail, int head, int weight)
{
	if (tail == head
		|| tail < 0 || tail >= numV
		|| head < 0 || head >= numV
		|| weight <= 0 || weight >= MAXWEIGHT)
		return false;
	return true;
}
//输入边
void inputEdge(Edge *edge, int numE)
{
	int i, tail, head, weight;
	cout << "输入边的起点、终点和权值" << endl;
	i = 0;
	while (i < numE)
	{
		cin >> tail >> head >> weight;
		while (!checkEdge(tail, head, weight))
		{
			cout << "输入错误!重新输入" << endl;
			cin >> tail >> head >> weight;
		}
		edge[i].tail = tail;
		edge[i].head = head;
		edge[i].weight = weight;
		i++;
	}
}
int cmp(const void *edge1, const void *edge2)
{
	return ((Edge*)edge1)->weight - ((Edge*)edge2)->weight;
}

//并查集的常见操作
/*
压缩查找
查找child的根节点
*/
int Find(int child, int *parent)
{
	if (parent[child] == child)
		return child;
	parent[child] = Find(parent[child], parent); //压缩路径
	return parent[child];
}
//合并
bool Union(Edge *e, int *parent, int *childs)
{
	//处于同一个棵树中,则不能合并,否则会出现环
	int root1, root2;
	root1 = Find(e->tail, parent);
	root2 = Find(e->head, parent);
	if (root1 != root2)
	{
		//把小树合并到大树根下
		if (childs[root1] > childs[root2])
		{
			parent[root2] = root1;
			childs[root1] += childs[root2] + 1;
		}
		else
		{
			parent[root1] = root2;
			childs[root2] += childs[root2] + 1;
		}
		return true;
	}
	return false;
}
/*
Kruskal算法
求最小生成树
*/
void Kruskal(int numV, int numE)
{
	//边的集合
	Edge *edge = new Edge[numE];
	inputEdge(edge, numE);
	/*
	parent[i]是顶点i的父顶点
	childs[i]是顶点i的孩子数
	child的复数是children,这里的childs是杜撰的
	*/
	int *parent = new int[numV];
	int *childs = new int[numV];
	//初始化
	for (int i = 0; i < numV; i++)
	{
		/*
		初始时,每一个顶点都是根,且无孩子
		把每个顶点的的父节点设置为自身下标,也是标明类别
		*/
		parent[i] = i;
		childs[i] = 0;
	}
	//对边按权值进行从小到大排序
	qsort(edge, numE, sizeof(Edge), cmp);
	int i, count;
	i = count = 0;
	cout << "最小生成树的边..." << endl;
	while (i < numE)
	{
		if (Union(&edge[i], parent, childs))
		{
			cout << edge[i].tail << "---" << edge[i].head << endl;
			count++;
		}
		if (count == numV - 1)  //边数达到要求
			break;
		i++;
	}
	if (count != numV - 1)
		cout << "此图为非连通图!无法构成最小生成树。" << endl;
	delete[]edge;
	delete[]parent;
	delete[]childs;
}
//检测顶点数和边数
bool checkVE(int numV, int numE)
{
	if (numV <= 0 || numE <= 0 || numE > numV*(numV - 1) / 2)
		return false;
	return true;
}
int main()
{
	cout << "******Kruskal***by David***" << endl;
	cout << "输入顶点数、边数 ";
	cin >> numV >> numE;
	while (!checkVE(numV, numE))
	{
		cout << "输入数据有问题!重新输入 ";
		cin >> numV >> numE;
	}
	cout << endl << "Kruskal..." << endl;
	Kruskal(numV, numE);
	system("pause");
	return 0;
}
运行





转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/38414683


若有所帮助,顶一个哦!


专栏目录:




1
1
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值