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

并查集有很多经典的应用。在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

其中一个非常经典的应用是最小生成树的Kruskal算法。给定一个具有n个节点的连通图,它的生成树是原图的一个子图,包含所有n个节点,且有保持图连通的最少的边(n-1条边)。边权值最小的生成树是最小生成树。

kruskal算法是一个贪心算法,把所有的边按权值从小到大依次考虑,如果当前边加进生成树中会出现回路,则丢弃当前边,否则添加当前边。

克鲁斯卡尔算法(Kruskal‘s algorithm)是两个经典的最小生成树算法的较为简单理解的一个。这里面充分体现了贪心算法的精髓。大致的流程可以用一个图来表示。这里的图的选择借用了Wikipedia上的那个。非常清晰且直观。

首先第一步,我们有一张图,有若干点和边

第一步我们要做的事情就是将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择。

排序完成后,我们率先选择了边AD。这样我们的图就变成了

.

.

.

.

.

.

第二步,在剩下的边中寻找。我们找到了CE。这里边的权重也是5

.

.

.

.

.

.

依次类推我们找到了6,7,7。完成之后,图变成了这个样子。

.

.

.

.

.

.

下一步就是关键了。下面选择那条边呢? BC或者EF吗?都不是,尽管现在长度为8的边是最小的未选择的边。但是他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以我们不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。

最后就剩下EG和FG了。当然我们选择了EG。最后成功的图就是下图:

.

.

.

.

.

.

到这里所有的边点都已经连通了,一个最小生成树构建完成。

Kruskal算法的时间复杂度由排序算法决定,若采用快排则时间复杂度为O(N log N)。

现在从并查集的角度考虑这个问题。初始时我们把所有节点自身初始化为一个集合。每次添加一条边进入最小生成树时,实际上是把这条边的两个节点所在的集合合并。如果添加当前边之后会产生回路,实际上是指当前边的两个节点所在的集合是一样的。所以实际上可以把边按权值从小到大排序,然后逐个合并边的两个节点的集合,如果节点集合一样,就添加了一条新边,否则直接忽略。

先定义表示图的数据结构,这里只需要存储边就可以了,以下是边的结构体数组,并且包含必要头文件:

#include <iostream>
#include <algorithm>
using namespace std;

const int mxsz = 1000;
struct Edge{
	int st, ed, len;
} edge[mxsz];

然后可以用并查集实现Kruskal算法了,并查集的代码在上一篇博客中写过,下面有链接,包含一个边排序的比较函数,并且以上图例子作为输入进行测试:

bool cmp(const Edge &a, const Edge &b) { return a.len < b.len; }
void Kruskal()
{
	//输入图
	int nodeNum, edgeNum; //节点数量、边的数量
	scanf("%d%d", &nodeNum, &edgeNum);
	for (int i = 0; i < edgeNum; i++) //输入边
	{
		int st, ed, len;
		scanf("%d%d%d", &st, &ed, &len);
		edge[i].st = st, edge[i].ed = ed, edge[i].len = len;
	}

	//计算最小生成树权值
	sort(edge, edge + edgeNum, cmp); //边排序
	n = nodeNum;	makeSet();  //初始化
	int weight = 0;
	for (int i = 0; i < edgeNum; i++) //遍历边
		if (unionSet(edge[i].st, edge[i].ed) == 1) //合并边的两个节点所在集合
			weight += edge[i].len; //如果节点集合不同,加入最小生成树中
	printf("最小生成树权值:%d\n", weight);

}

http://blog.csdn.net/djd1234567/article/details/48057527 并查集简要分析

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-07 04:50:53

并查集扩展之最小生成树Kruskal算法的相关文章

并查集(union-find set)与Kruskal算法

并查集 并查集处理的是集合之间的关系,即‘union' , 'find' .在这种数据类型中,N个不同元素被分成若干个组,每组是一个集合,这种集合叫做分离集合.并查集支持查找一个元素所属的集合和两个元素分别所属的集合的合并. 并查集支持以下操作: MAKE(X):建立一个仅有成员X的新集合. UNION(X,Y):将包含X和Y的动态集合合并为一个新集合S,此后该二元素处于同一集合. FIND(X):返回一个包含X的集合. 注意:并查集只能进行合并操作,不能进行分割操作. 并查集的实现原理 并查集

最小生成树 kruskal算法简介

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

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算法

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

UVA - 12232 Exclusive-OR (并查集扩展偏离向量)

Description You are not given n non-negative integersX0,X1,..., Xn-1 less than220, but they do exist, and their values never change. I'll gradually provide you some facts about them, and ask you some questions. There are two kinds of facts, plus one

无向图最小生成树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算法,写下随笔. 对于稀疏图来说,用Kruskal写最小生成树效率更好,加上并查集,可对其进行优化. Kruskal算法的步骤: 1.对所有边进行从小到大的排序. 2.每次选一条边(最小的边),如果如果形成环,就不加入(u,v)中,否则加入.那么加入的(u,v)一定是最佳的. 并查集: 我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点.而具体的连通方式无关紧要,好比集合中的元素没有先后顺序之分,只有"属于"与"不属于"的区别.

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

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