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

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),边数已达到要求,算法结束。终于的图是这种

算法逻辑人非常easy理解。但用代码推断当前边是否会引起环的出现。则非常棘手。

算法说明

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

于是问题又来了,怎样推断两个顶点是否属于同一个连通分量呢?这个能够參照并查集的做法解决。它的思路是:假设两个顶点的根节点是一样的,则显然是属于同一个连通分量。

这也相同暗示着:在增加新边时,要更新父节点。

详细细节看代码:

代码

#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

若有所帮助,顶一个哦。

专栏文件夹:

时间: 2024-10-19 18:16:41

数据结构:最小生成树--Kruskal算法的相关文章

SOJ4339 Driving Range 最小生成树 kruskal算法

典型的最小生成树 然后求最大的一条边 附上链接 http://cstest.scu.edu.cn/soj/problem.action?id=4339 需要注意的是有可能有 "IMPOSSIBLE" 的情况 这里用一个flag标记 记录所并的节点 只有flag = n时才能成功 负责就 "IMPOSSIBLE" 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring&g

最小生成树 kruskal算法简介

生成树--在一个图中的一个联通子图  使得所有的节点都被(访问) 最小生成树 (MST) 即联通子图的总代价(路程)最小 已知的一个图 有n个点 m条边 kruskal的算法如下 先对边从小到大排序 从最小的边起,不停的合并这条边的两个节点到一个集合,如果这条边的两个节点已经在一个集合里,则无视,否则形成回路(显然错误)直到所有的节点并到一个集合里 这里需要用到并查集来合并节点 1 int cmp(const int i,const int j) { 2 return w[i] < w[j];

最小生成树 Kruskal算法

Kruskal算法 1.概览 Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表.用来解决同样问题的还有Prim算法和Boruvka算法等.三种算法都是贪婪算法的应用.和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效. 2.算法简单描述 1).记Graph中有v个顶点,e个边 2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边 3).将原图Graph中所有e个边按权值从小到大排序 4)

无向图最小生成树Kruskal算法

问题 最小生成树的Kruskal算法 描述:有A.B.C.D四个点,每两个点之间的距离(无方向)是(第一个数字是两点之间距离,后面两个字母代表两个点):(1,'A','B'),(5,'A','C'),(3,'A','D'),(4,'B','C'),(2,'B','D'),(1,'C','D') 生成边长和最小的树,也就是找出一种连接方法,将各点连接起来,并且各点之间的距离和最小. 思路说明: Kruskal算法是经典的无向图最小生成树解决方法.此处列举两种python的实现方法.这两种方法均参考

最小生成树Kruskal算法的提出者Joseph Bernard Kruskal,Jr.

熟悉算法中的最小生成树的朋友都晓得有一个Kruskal算法,这个算法就是由题目中那个名字很长的人提出的.因为他功绩卓越,尊称他为Kruskal. Kruskal生于1928年1月29日,卒于2010年9月19日,美国人,维基里的词条中包含的头衔是:数学家.统计学家.计算机科学家.心理测量学专家. kruskal分别就读过芝加哥大学和普林斯顿大学,1954年获得博士学位. 下面链接中是一篇几年他的文章,从中可以了解他的更多成就.顺便说一句,他的父母和兄弟也都很牛. http://pan.baidu

模板——最小生成树kruskal算法+并查集数据结构

并查集:找祖先并更新,注意路径压缩,不然会时间复杂度巨大导致出错/超时 合并:(我的祖先是的你的祖先的父亲) 找父亲:(初始化祖先是自己的,自己就是祖先) 查询:(我们是不是同一祖先) 路径压缩:(每个点只保存祖先,不保存父亲) 最小生成树kruskal:贪心算法+并查集数据结构,根据边的多少决定时间复杂度,适合于稀疏图 核心思想贪心,找到最小权值的边,判断此边连接的两个顶点是否已连接,若没连接则连接,总权值+=此边权值,已连接就舍弃继续向下寻找: 并查集数据结构程序: #include<ios

最小生成树——Kruskal算法

前面介绍了最小生成树和Prim算法,这篇博客继续记录Kruskal算法的相关内容. 算法思想: 1. 先将所有边按权值由小到大排序: 2. 从边集中选出第一条边(即权值最小的边),如果与树中现有的边不构成环,则将其加入树中: 3. 重复步骤2直至树中有n-1条边. 在实现上述算法之前,要先解决三个问题: 1. 如何表示一条边? 虽然我们尽量简化情景方便实现,但是边还是不能像节点一样简单地用一个数表示,因为它有三个必备的属性:起点.终点和权值.因此,我们创建以下结构体来表示边: 1 // 定义表示

并查集扩展之最小生成树Kruskal算法

并查集有很多经典的应用.在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中. 其中一个非常经典的应用是最小生成树的Kruskal算法.给定一个具有n个节点的连通图,它的生成树是原图的一个子图,包含所有n个节点,且有保持图连通的最少的边(n-1条边).边权值最小的生成树是最小生成树. kruskal算法是一个贪心算法,把所有的边按权值从小到大依次考虑,如果当前边加进生成树中会出现回路

ZOJ 1718 POJ 2031 Building a Space Station 修建空间站 最小生成树 Kruskal算法

题目链接:ZOJ 1718 POJ 2031 Building a Space Station 修建空间站 Building a Space Station Time Limit: 2 Seconds      Memory Limit: 65536 KB You are a member of the space station engineering team, and are assigned a task in the construction process of the statio