- 浏览: 508664 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
jkxydp:
算法运行的结果根本就不对。
BM算法. -
soarwindzhang:
感谢博主的分享,我今天看了您的UFSET非递归的路径压缩时感觉 ...
并查集 -
zhangning290:
楼主好像只考虑了坏字符规则,。没有考虑好后缀
BM算法. -
lsm0622:
文字描述有错误 误导新学者
求有向图的强连通分量(scc):Tarjan算法 -
knightchen:
博主,你太强了!这篇文章对我学习C++多线程很有帮助!谢谢
并发学习之一_windows下ZThread在CodeBlocks上的安装与配置
1,什么是并查集?
并查集,就是Union-Find Set,也称不相交集合 (Disjoint Set),用于处理一些不相交集合(Disjoint Sets)的合并及查询问题,如其求无向图的连通分量个数等.最完美的应用当属:实现Kruskar算法求最小生成树.
2,并查集的主要操作有:
(1)makeSet(x):把每一个元素初始化为一个集合
初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变).
(2)findSet(x):查找一个元素所在的集合
查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先.这个才是并查集判断和合并的最终依据.
判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。
(3)Union(x,y):合并x,y所在的两个集合
合并两个不相交集合操作很简单:
利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。
3,并查集的优化:
(1)Find_Set(x)时 路径压缩
寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度,有没有办法减小这个复杂度呢?
这就是路径压缩,即当我们经过"递推"找到祖先节点后,"回溯"的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了.
(2)Union(x,y)时 按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。
实现代码:
4,应用
(1)http://acm.pku.edu.cn/JudgeOnline/problem?id=1611
(2)http://acm.pku.edu.cn/JudgeOnline/problem?id=2524
分析:实质就是求连通分量的个数
思路:每合并一次,将个数减1,即可得到最后结果.
(3)http://acm.pku.edu.cn/JudgeOnline/problem?id=2236
思路:并查集。修复一台电脑就标记为修复过了,然后跟其他已经修复过的电脑进行合并,但是合并的条件必须是两台电脑的距离小于D。当要测试两台电脑时,就是判断他们的根结点是否一样.
并查集,就是Union-Find Set,也称不相交集合 (Disjoint Set),用于处理一些不相交集合(Disjoint Sets)的合并及查询问题,如其求无向图的连通分量个数等.最完美的应用当属:实现Kruskar算法求最小生成树.
2,并查集的主要操作有:
(1)makeSet(x):把每一个元素初始化为一个集合
初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变).
(2)findSet(x):查找一个元素所在的集合
查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先.这个才是并查集判断和合并的最终依据.
判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。
(3)Union(x,y):合并x,y所在的两个集合
合并两个不相交集合操作很简单:
利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。
3,并查集的优化:
(1)Find_Set(x)时 路径压缩
寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度,有没有办法减小这个复杂度呢?
这就是路径压缩,即当我们经过"递推"找到祖先节点后,"回溯"的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了.
(2)Union(x,y)时 按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。
实现代码:
#defince VMAX 100 //节点个数 int father[VMAX]; //father[x]:元素x的父节点 int rank[VMAX]; //rank[x]:x元素的秩 //初始化操作 void makeSet(int n) { for(i = 0; i < n; i++) { father[i] = i; //根据实际情况指定的父节点可变化 rank[i] = 0; //根据实际情况初始化秩也有所变化 } } //查找操作:两种实现方式 //递归实现 int findSetR(int x) { if(x!=father[x]) //通过回溯,完成路径压缩 father[x]=findSetR(father[x]); return father[x]; } //非递归实现 int findSet(int x) { int r=x; while(r!=father[r]) r=father[r]; //路径压缩:把x上面的所有节点的父节点都指向祖先节点 int temp; while(x!=r) { temp=father[x]; father[temp]=r; x=temp; } return r; } //合并操作 void UnionR(int x,int y) { x=findSetR(x); y=findSetR(y); if(x==y) return; if( rank[x]>rank[y] ) //通常将元素少的集合合并到元素多的集合 father[y]=x; else father[x]=y; if( rank[x]==rank[y] ) rank[y]++; } void Union(int x,int y) { x=findSet(x); y=findSet(y); if(x==y) return; if( rank[x]>rank[y] ) //通常将元素少的集合合并到元素多的集合 father[y]=x; else father[x]=y; if( rank[x]==rank[y] ) rank[y]++; }
4,应用
(1)http://acm.pku.edu.cn/JudgeOnline/problem?id=1611
#include<iostream> using namespace std; int n, m, i, j; int father[30005], num[30005]; void makeSet(int n) { for(i = 0; i < n; i++) { father[i] = i; //使用本身做根 num[i] = 1; //记录集合的数目 } } int findSetR(int x) { if(father[x] != x) //合并后的树的根是不变的 { father[x] = findSetR(father[x]); } return father[x]; } //非递归实现 int findSet(int x) { int r=x; while(r!=father[r]) r=father[r]; //路径压缩:把x上面的所有节点的父节点都指向祖先节点 int temp; while(x!=r) { temp=father[x]; father[temp]=r; x=temp; } return r; } void Union(int a, int b) { int x = findSet(a); int y = findSet(b); if(x == y) { return; } if(num[x] <= num[y]) { father[x] = y; num[y] += num[x]; } else { father[y] = x; num[x] += num[y]; } } void UnionR(int a, int b) { int x = findSetR(a); int y = findSetR(b); if(x == y) { return; } if(num[x] <= num[y]) { father[x] = y; num[y] += num[x]; } else { father[y] = x; num[x] += num[y]; } } int main() { while(scanf("%d %d", &n, &m)!=EOF && n != 0) //学生数 小组数 { makeSet(n); for(i = 0; i < m; i++) { int count, first, b; //小组成员数目 成员编号0到n-1 scanf("%d %d",&count, &first); for(j = 1; j < count; j++) { scanf("%d",&b); Union(first,b); } } //0所在集合的数目,就是可能人群数 printf("%d\n",num[findSet(0)]); } return 0; }
(2)http://acm.pku.edu.cn/JudgeOnline/problem?id=2524
分析:实质就是求连通分量的个数
思路:每合并一次,将个数减1,即可得到最后结果.
#include<iostream> using namespace std; int father[50005], rank[50005]; int maxN; void makeSet(int n) { for(int i = 0; i < n; i++) { father[i] = i; rank[i] = 0; } } int findSet(int x) { int r=x; while(r!=father[r]) r=father[r]; int temp; while(x!=r) { temp=father[x]; father[temp]=r; x=temp; } return r; } void Union(int x,int y) { x=findSet(x); y=findSet(y); if(x==y) return; maxN--; if( rank[x]>rank[y] ) father[y]=x; else father[x]=y; if( rank[x]==rank[y] ) rank[y]++; } int main() { int Case = 1; int m; //总人数 问题数 while(scanf("%d %d", &maxN, &m),maxN!=0) { makeSet(maxN); int a, b; for(int i = 0; i < m; i++) { //a和b信仰相同 scanf("%d %d",&a, &b); a--,b--; Union(a,b); } printf("Case %d: %d\n",Case++,maxN); } return 0; }
(3)http://acm.pku.edu.cn/JudgeOnline/problem?id=2236
思路:并查集。修复一台电脑就标记为修复过了,然后跟其他已经修复过的电脑进行合并,但是合并的条件必须是两台电脑的距离小于D。当要测试两台电脑时,就是判断他们的根结点是否一样.
#include <iostream> #include <math.h> using namespace std; #define size 1002 int father[size]; int rank[size]; bool isVisit[size]; struct coordinate { double x; double y; }locate[size]; void makeSet(int x)//创建集合 { father[x] = x; rank[x] = 1; isVisit[x] = false; } int findSet(int x)//寻找根结点 { int r = x; int temp; while(r!=father[r]) r = father[r]; while (x!=r) { temp = father[x]; father[temp] = r; x = temp; } return r; } void Union(int a,int b)//合并集合 { a = findSet(a); b = findSet(b); if(rank[a]>rank[b]) father[b] = a; else father[a] = b; if(rank[a] == rank[b]) rank[b]++; } bool IsOk(int a,int b,long distance)//判断两台电脑是否可以连通 { double k; k = sqrt((locate[a].x-locate[b].x)*(locate[a].x-locate[b].x)+(locate[a].y-locate[b].y)*(locate[a].y-locate[b].y)); return k<=distance; } int main() { char commander; int computer; int p1,p2; int i; long distance; cin>>computer>>distance; //pc数目,有效距离 for(i=1;i<=computer;i++) makeSet(i); for (i=1;i<=computer;i++) //输入每台pc的位置 cin>>locate[i].x>>locate[i].y; while (cin>>commander) { if (commander == 'O') { cin>>p1; isVisit[p1] = true; /*查看修复的电脑是否可以加入到并查集中*/ for(i=1;i<=computer;i++) { if(isVisit[i]&&i!=p1) if(IsOk(p1,i,distance)) //满足通讯条件,合并 if(findSet(p1)!=findSet(i)) Union(p1,i); } } else { cin>>p1>>p2; if(findSet(p1)==findSet(p2)) cout<<"SUCCESS"<<endl; else cout<<"FAIL"<<endl; } } return 0; }
评论
1 楼
soarwindzhang
2012-08-08
感谢博主的分享,我今天看了您的UFSET非递归的路径压缩时感觉稍微有点小问题,
while(x!=r)
{
temp=father[x];
father[temp]=r;
x=temp;
}
这里的father[temp] = r是不是应该是father[x] = r呢,否则没有达到把他的子孙都连到root上的效果,而且这样一次就停止了,虽然也压缩了一小部分
while(x!=r)
{
temp=father[x];
father[temp]=r;
x=temp;
}
这里的father[temp] = r是不是应该是father[x] = r呢,否则没有达到把他的子孙都连到root上的效果,而且这样一次就停止了,虽然也压缩了一小部分
发表评论
-
为什么堆排比快排慢
2010-12-16 15:25 3018[节选]http://mindhacks.cn/200 ... -
使用map和hash_map的效率问题
2010-12-09 19:49 68891,选择map容器,是为了更快的从关键字查找到相关的对象。 与 ... -
斐波那契堆
2010-07-04 11:37 181, http://kmplayer.iteye.com/ad ... -
斐波那契堆
2010-07-04 11:36 1421学习的地方: http://en.wikipedia.org/ ... -
Sunday算法
2010-07-02 11:38 89811,Sunday算法是Daniel M.Sunday于1990 ... -
BM算法.
2010-07-02 10:53 77051,BM算法是Boyer-Moore算法的简称,由Boyer ... -
优先级队列
2010-06-17 23:15 11931,优先级队列是不同于 ... -
计数排序
2010-05-04 16:59 7821,计数排序是一个非基于比较的线性时间排序算法。 它对输入的数 ... -
归并排序
2010-05-04 16:47 9011,归并排序是建立在归并操作上的一种有效的排序算法。该算法是采 ... -
求大数阶层
2010-05-04 16:40 13411,思想类似于大数的加减乘法. 数组的每个元素维护一个4位数. ... -
基数排序
2010-05-03 16:45 889详细解释参考:http://en.wikipedia.org/ ... -
求两个数组的中位数
2010-05-02 12:08 41051,题目 有两个数组,均已经按升序排列好,编程序计算这两个数组 ... -
imba的bit向量
2010-04-22 17:30 9311,先给出一个模板. #include <iostr ... -
编写自己的malloc
2010-04-19 17:03 15091,如果一个程序大量调用malloc,程序的很多时间将会消耗在 ... -
快速排序
2010-04-01 13:50 7361,给出一个实现实例. #include <iost ... -
md5算法
2010-03-31 10:35 2665Message Digest Algorithm MD5( ... -
堆排序
2010-03-23 10:48 8901,"堆"定义 n个关 ... -
改进的线性筛法_寻找素数
2010-03-03 10:41 16111,实例代码: #include <iostream ... -
求有向图的强连通分量(scc):Tarjan算法
2010-02-28 15:41 143061,在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强 ... -
最近公共祖先LCA:Tarjan算法
2010-02-28 15:25 178381,并查集+dfs 对整个树进行深度优先遍历,并在遍历的过程中 ...
相关推荐
数据结构中的并查集(Disjoint Set)是一种用于处理元素集合的分割问题的数据结构,它主要支持两种操作:合并(union)与查找(find)。在实际应用中,尤其是在算法竞赛(ACM)和图论中,它被广泛用于解决快速查询和...
### 并查集知识点概述 #### 一、并查集定义与基本概念 **并查集**(Union-Find Set)是一种数据结构,用于处理一些不相交集合的合并及查询问题,通常用来解决动态连通性问题。并查集支持两种主要的操作:**并操作*...
并查集是一种在图论和算法设计中常用的离散数学结构,主要用于处理一些不相交集合的合并与查询问题。这个压缩包文件“团伙数据”显然包含了一些与并查集算法相关的练习数据和可能的题解,目的是帮助学习者更好地理解...
综上所述,"并查集入门学习并查集"的资源可能是针对初学者的教程,通过“并查集.pdf”这个文档,你将能够深入理解并查集的基本概念、操作原理以及如何在实际问题中应用。对于希望提升算法能力,尤其是参与ACM竞赛的...
并查集是一种在大型无向图中查找元素所属连通分量的数据结构,常用于解决合并与查询的问题。它的核心思想是将元素组织成一棵树形结构,每个元素都有一个父节点,根节点代表了一个连通分量。并查集的主要操作包括...
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复...
并查集是一种在图论和算法设计中常用的离散数学结构,主要用来处理一些不相交集合的合并与查询问题。在本数据压缩包中,主题聚焦于家谱数据,可以推断出这些数据可能包含了家庭成员之间的关系,通过并查集这种数据...
### 并查集模版详解 #### 一、并查集简介 并查集是一种用于处理元素集合的动态数据结构,常被应用于图论、计算机网络、数据挖掘等领域。其核心功能是支持两种操作:合并两个集合(并操作)与查询某个元素所属的...
并查集是一种在大型无向图中查找连接组件的数据结构,它在计算机科学中有着广泛的应用,尤其是在图论和算法设计中。这个压缩包文件"division"可能包含了一个C++实现并查集的示例代码,特别指出是为Visual Studio ...
并查集是一种数据结构,主要用于处理一些不相交集合的合并与查询问题。在并查集中,我们将一组对象划分为多个不相交的集合,每个集合由一个代表元素标识。这个数据结构支持两种主要操作: 1. **Find** 操作:查询...
并查集是一种用于处理不相交集合操作的数据结构,它主要包含两个基本操作:查找(Find)和合并(Union)。在并查集中,通常用森林的结构来表示各个集合,每棵树代表一个集合,树的根节点代表集合的标识。 **查找...
并查集是一种在大型无向图中查找元素所属集合的数据结构,它主要应用于不经常进行连接,但频繁查询是否属于同一集合的问题。在并查集中,我们通常关注两个操作:`Find` 和 `Union`。`Find` 操作用于确定元素所属的...
并查集是一种在分散的数据元素中寻找连接关系的数据结构,常用于处理一些不相交集合的合并与查询问题。在图论中,它可以帮助我们快速判断两个节点是否属于同一个连通分量,或者将两个连通分量合并。在本教程中,我们...
并查集是一种数据结构,主要用于处理不相交集合的合并与查询问题。它在很多算法竞赛,如信息学奥林匹克竞赛(OI)和全国青少年信息学奥林匹克联赛(NOIP)中有着广泛的应用。朱全民教授的讲解主要围绕并查集的基本...
并查集模板并查集模板并查集模板并查集模板并查集模板并查集模板
并查集是一种用于处理集合合并与查询的抽象数据类型,主要应用于解决连通性问题,例如判断图中的两个节点是否在同一连通分量内。在本解题报告中,我们将探讨并查集算法在PKU(北京大学)的三道题目:PKU1182、PKU...
深入理解并查集算法,细致讲解,专业老师,一步到位 。
并查集是一种在图论和数据结构中广泛使用的算法,主要应用于处理不相交集合的合并与查询。在ACM(国际大学生程序设计竞赛)中,掌握并查集的运用对于解决某些特定类型的问题至关重要。这个压缩包包含了全面的并查集...
### 并查集算法思想详解 #### 一、并查集基本概念 并查集是一种数据结构,主要用于处理一些不交集的合并及查询问题,它支持两种操作: 1. **合并**(Union):将两个不同的集合合并成一个集合。 2. **查找**...
### 并查集C/C++代码实现 #### 知识点概述 并查集是一种用于处理元素集合的动态集合问题的数据结构,它支持两种基本的操作:**并**和**查**。并查集的主要用途是判断一个元素是否属于某个集合,并且能够有效地将两...