并查集算法学习(转)
2016-08-30 19:11
253 查看
我读的博客地址是这个:
http://blog.csdn.net/dellaserss/article/details/7724401/
但是忍不住自己再写一遍顺便加入一些自己的思考。
这是我见过的最好的并查集教程,作者不知道是谁,但是读一遍,基本上并查集这个概念,以及如何快速写出代码来,基本没有问题了!
并查集是一种用来管理元素分组情况的数据结构。
并查集的结构:
每个组对应一棵树。每个元素对应一个结点。
而树的形状,结点之间谁是父亲等都不重要。
并查集是维护属于同一组的数据结构。
数组pre[ ]记录了每个点的前导点事什么,函数find是查找,join是合并。
以上是官面儿的解释,下面是重点。
解释find函数,这个过程实在太欢乐了。
话说江湖上散落着各式各样的大侠,有上千个之多。他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。但大侠们有一个优点就是讲义气,绝对不打自己的朋友。而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。这样一来,江湖上就形成了一个一个的群落,通过两两之间的朋友关系串联起来。而不在同一个群落的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢?
我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物,这样,每个圈子就可以这样命名“齐达内朋友之队”“罗纳尔多朋友之队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。
但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长,要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?”这样一来,队长面子上挂不住了,而且效率太低,还有可能陷入无限循环中。于是队长下令,重新组队。队内所有人实行分等级制度,形成树状结构,我队长就是根节点,下面分别是二级队员、三级队员。每个人只要记住自己的上级是谁就行了。遇到判断敌友的时候,只要一层层向上问,直到最高层,就可以在短时间内确定队长是谁了。由于我们关心的只是两个人之间是否连通,至于他们是如何连通的,以及每个圈子内部的结构是怎样的,甚至队长是谁,并不重要。所以我们可以放任队长随意重新组队,只要不搞错敌友关系就好了。于是,门派产生了。
int pre[1000]数组记录了每个大侠的上级,大侠们从1或0开始编号,pre[15] = 3就表示15号大侠的上级是3号大侠。如果一个人的上级就是他自己,说明他就是掌门人了,查询到此为止。也有孤家寡人自成一派的。每个人只认识自己的上级,要想知道掌门是谁,只能一级一级查上去,find函数就是找掌门人用的。
join函数的含义也很有趣:
join函数,就是在两个点之间连一条线,这样一来,原先它们所在的两个板块的所有点就都可以互通了。这在图上很好办,画条线就行了。但我们现在是用并查集来描述武林中的状况的,一共只有一个pre[]数组,该如何实现呢? 还是举江湖的例子,假设现在武林中的形势如图所示。虚竹小和尚与周芷若MM是我非常喜欢的两个人物,他们的终极boss分别是玄慈方丈和灭绝师太,那明显就是两个阵营了。我不希望他们互相打架,就对他俩说:“你们两位拉拉勾,做好朋友吧。”他们看在我的面子上,同意了。这一同意可非同小可,整个少林和峨眉派的人就不能打架了。这么重大的变化,可如何实现呀,要改动多少地方?其实非常简单,我对玄慈方丈说:“大师,麻烦你把你的上级改为灭绝师太吧。这样一来,两派原先的所有人员的终极boss都是师太,那还打个球啊!反正我们关心的只是连通性,门派内部的结构不要紧的。”玄慈一听肯定火大了:“我靠,凭什么是我变成她手下呀,怎么不反过来?我抗议!”抗议无效,上天安排的,最大。反正谁加入谁效果是一样的,我就随手指定了一个。这段函数的意思很明白了吧?
考察路径压缩算法:
http://blog.csdn.net/dellaserss/article/details/7724401/
但是忍不住自己再写一遍顺便加入一些自己的思考。
这是我见过的最好的并查集教程,作者不知道是谁,但是读一遍,基本上并查集这个概念,以及如何快速写出代码来,基本没有问题了!
并查集是一种用来管理元素分组情况的数据结构。
并查集的结构:
每个组对应一棵树。每个元素对应一个结点。
而树的形状,结点之间谁是父亲等都不重要。
并查集是维护属于同一组的数据结构。
构成
并查集由一个整数型的数组和两个函数构成。数组pre[ ]记录了每个点的前导点事什么,函数find是查找,join是合并。
int pre[1000];//记录每个点的前导点是什么 int find(int x) //查找根节点 { int r = x; while(pre[r] != r) //返回根节点r r = pre[r]; int i = x,j;//路径压缩 while(i != r) { j = pre[i]; pre[i] = r; i = j; } return r; } void join(int x, int y) { //判断x,y是否连通,如果连通就啥也不做,否则就把这个两个分支合并起来 int fx = find(x); int fy = find(y); if(fx != fy) pre[fx] = fy; }
以上是官面儿的解释,下面是重点。
解释find函数,这个过程实在太欢乐了。
话说江湖上散落着各式各样的大侠,有上千个之多。他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。但大侠们有一个优点就是讲义气,绝对不打自己的朋友。而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。这样一来,江湖上就形成了一个一个的群落,通过两两之间的朋友关系串联起来。而不在同一个群落的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢?
我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物,这样,每个圈子就可以这样命名“齐达内朋友之队”“罗纳尔多朋友之队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。
但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长,要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?”这样一来,队长面子上挂不住了,而且效率太低,还有可能陷入无限循环中。于是队长下令,重新组队。队内所有人实行分等级制度,形成树状结构,我队长就是根节点,下面分别是二级队员、三级队员。每个人只要记住自己的上级是谁就行了。遇到判断敌友的时候,只要一层层向上问,直到最高层,就可以在短时间内确定队长是谁了。由于我们关心的只是两个人之间是否连通,至于他们是如何连通的,以及每个圈子内部的结构是怎样的,甚至队长是谁,并不重要。所以我们可以放任队长随意重新组队,只要不搞错敌友关系就好了。于是,门派产生了。
int pre[1000]数组记录了每个大侠的上级,大侠们从1或0开始编号,pre[15] = 3就表示15号大侠的上级是3号大侠。如果一个人的上级就是他自己,说明他就是掌门人了,查询到此为止。也有孤家寡人自成一派的。每个人只认识自己的上级,要想知道掌门是谁,只能一级一级查上去,find函数就是找掌门人用的。
int find(int x) //查找掌门人 { int r = x; //委托r去找掌门 while(pre[r] != r) //如果r的上级不是r自己 r = pre[r]; //r就接着找他的上级,直到找到掌门为止 return r; //掌门来了 }
join函数的含义也很有趣:
join函数,就是在两个点之间连一条线,这样一来,原先它们所在的两个板块的所有点就都可以互通了。这在图上很好办,画条线就行了。但我们现在是用并查集来描述武林中的状况的,一共只有一个pre[]数组,该如何实现呢? 还是举江湖的例子,假设现在武林中的形势如图所示。虚竹小和尚与周芷若MM是我非常喜欢的两个人物,他们的终极boss分别是玄慈方丈和灭绝师太,那明显就是两个阵营了。我不希望他们互相打架,就对他俩说:“你们两位拉拉勾,做好朋友吧。”他们看在我的面子上,同意了。这一同意可非同小可,整个少林和峨眉派的人就不能打架了。这么重大的变化,可如何实现呀,要改动多少地方?其实非常简单,我对玄慈方丈说:“大师,麻烦你把你的上级改为灭绝师太吧。这样一来,两派原先的所有人员的终极boss都是师太,那还打个球啊!反正我们关心的只是连通性,门派内部的结构不要紧的。”玄慈一听肯定火大了:“我靠,凭什么是我变成她手下呀,怎么不反过来?我抗议!”抗议无效,上天安排的,最大。反正谁加入谁效果是一样的,我就随手指定了一个。这段函数的意思很明白了吧?
void join(int x, int y) //我想让虚竹和周芷若做朋友 { int fx = find(x); //虚竹的老大是玄慈 int fy = find(y); //芷若mm的老大是灭绝 if(fx != fy) //玄慈和方丈不是同一个人 pre[fx] = fy; //委屈方丈当了师太的手下 }
考察路径压缩算法:
int find(int x) //查找x的掌门人 { int r = x; //委托r去找掌门 while(pre[r] != r) //如果r的上级不是r自己 r = pre[r]; //r就接着找他的上级,直到找到掌门为止 //路径压缩:想把x和x的上级,x的上上级直到掌门的首座全都变成掌门的直系弟子 int i = x,j; //i初始代指x while(i != r) //如果i不是掌门 { j = pre[i]; //找到i的上级,并赋值给j pre[i] = r;//i的老大现在改为掌门人 i = j;//再往上搞j,i曾经的老大,直到搞到掌门人自己为止 } return r; //掌门来了 }
相关文章推荐
- 【算法学习】【数据结构】并查集
- 【算法学习笔记】44. 并查集补充 SJTU OJ 3015 露子的星空
- 算法:并查集学习笔记
- 算法学习 并查集(笔试题目:找同伙)
- 算法学习基础篇(四):数据结构(堆、二叉搜索树、并查集)
- 并查集。路径压缩 算法运用学习(一)
- 算法模板学习专栏之并查集(一)入门
- 算法分析学习笔记(一) - 动态连通性问题的并查集算法(上)
- 算法学习之并查集
- poj 2524-小白算法学习 并查集 Ubiquitous Religions
- 数据结构与算法学习-并查集
- 算法学习——并查集
- 一个找亲戚游戏,引发了一场算法的学习——并查集
- 算法学习之并查集
- 算法学习——并查集
- 算法学习 并查集(Union-Find) (转)
- 链栈实现算法 - Java 学习笔记(26)
- 学习算法中
- 打造算法学习的实践平台
- (轉貼) STL算法学习 (C/C++) (STL)