当前位置:文档之家› 第十五周第十六周(并查集1,2)

第十五周第十六周(并查集1,2)

所以,也称为“并查集”
实现方法(1)
• 用编号最小的元素标记所在集合; • 定义一个数组 set[1..n] ,其中set[i] 表示
元素i 所在的集合;
i 1 2 3 4 5 6 7 8 9 10
Set(i) 1 2 1 4 2 6 1 6 2 2
不相交集合: {1,3,7}, {4}, {2,5,9,10}, {6,8}
优化后算法及效率
find2(x) {
r = x; while (set[r] != r)
r = set[r]; return r; }
最坏情况Θ(log N)
merge3(a,b) { if (height(a) == height(b)) {
height(a) = height(a) + 1; set[b] = a; } else if (height(a) < height(b)) set[a] = b; else set[b] = a; }
• 队内所有人实行分等级制度,形成树状结构,我 队长就是根节点,下面分别是二级队员、三级队 员。每个人只要记住自己的上级是谁就行了。遇 到判断敌友的时候,只要一层层向上问,直到最
高层,就可以在短时间内确定队长是谁了。由于 我们关心的只是两个人之间是否连通,至于他们 是如何连通的,以及每个圈子内部的结构是怎样
下面的例子,前两个是符合条件的,但是最后一个 却有两种方法从5到达8。
• 1.由于这里某个数字不一定会出现,所以 要设一个mark来标记数字是否出现过。每
次输入一对数字的关系则进行查找根结点 的函数,并通过合并函数来判断两个数是否 已经联通。
• 2.如果两个数字能查找到相同的根结点就证 明二者已经是相通的,再输入二者的关系 就变成有多条相通的路径了。这时候答案 肯定要输出“No”,如果两个数字不能查找 到共同的根结点把两数字所在的集合合并, 直到一组数据输入结束后,再进行判断, 是否输入的关系每个数字之间都有相通的 路径

r = set[r];
• i = x;
• while (i <> r) //本循环修改查找路径中所有节点
•{

j = set[i];

set[i] = r;

i = j;
•} }
路径压缩示意图
6
4
9
11 1 10 8
12 20
21 16
6 4 10 9 20 11 1 12 8 21 16
• 杭电1232 : 畅通工程 • 7157: 畅通工程【课后思考】 • 6619: 小希的迷宫 • 1106: 通信系统【课后思考】 • 5265:亲属关系【课后思考】
方法(1)——效率分析
find1(x) {
return set[x]; }
Θ(1)
Merge1(a,b) { i = min(a,b);
j = max(a,b); for (k=1; k<=N; k++) {
if (set[k] == j) set[k] = i;
} }
Θ(N)
有待改进?
• 对于“合并操作”,必须搜索全部元素! 树结构如何?
每个圈子就可以这样命名“齐达内朋友之 队”“罗纳尔多朋友之队”……两人只要互 相对一下自己的队长是不是同一个人,就 可以确定敌友关系了。
• 但是还有问题啊,大侠们只知道自己直接 的朋友是谁,很多人压根就不认识队长, 要判断自己的队长是谁,只能漫无目的的 通过朋友的朋友关系问下去:“你是不是 队长?你是不是队长?”这样一来,队长 面子上挂不住了,而且效率太低,还有可 能陷入无限循环中。于是队长下令,重新 组队。于是,门派产生了。
杭电ACM 1232 畅通工程
• 题目描述: 某省调查城镇交通状况,得到现有城镇道路 统计表,表中列出了每条道路直接连通的城 镇。省政府“畅通工程”的目标是使全省任 何两个城镇间都可以实现交通(但不一定有 直接的道路相连,只要互相间接通过道路可 达即可)。问最少还需要建设多少条道路?
6619 小希的迷宫
并查集
(Disjoint Set)
导引问题
在某个城市里住着n个人,任何两个认识 的人不是朋友就是敌人,而且满足:
• 我朋友的朋友是我的朋友; • 我敌人的敌人是我的朋友;
已知关于 n个人的m条信息(即某2个人 是朋友或者敌人),假设所有是朋友的人 一定属于同一个团伙,请计算该城市最多 有多少团伙?
• 话说江湖上散落着各式各样的大侠,有上千个 之多。他们没有什么正当职业,整天背着剑在 外面走来走去,碰到和自己不是一路人的,就 免不了要打一架。但大侠们有一个优点就是讲 义气,绝对不打自己的朋友。而且他们信奉 “朋友的朋友就是我的朋友”,只要是能通过 朋友关系串联起来的,不管拐了多少个弯,都 认为是自己人。
实现方法(2)
• 每个集合用一棵“有根树”表示
– 定义数组 set[1..n]
• set[i] = i , 则i表示本集合,并是集合对应树的根 • set[i] = j, j<>i, 则 j 是 i 的父节点.
i 1 2 3 4 5 6 7 8 9 10 Set(i) 1 2 3 2 1 3 4 3 3 4
}
Θ(1)
困惑
• 性能有本质改进? 如何避免最坏情况?
避免最坏情况
• 方法:将深度小的树合并到深度大的 树
• 实现:假设两棵树的深度分别为h1和 h2, 则合并后的树的高度h是:
– max(h1,h2), if h1<>h2. – h1+1, if h1=h2.
• 效果:任意顺序的合并操作以后,包 含k个节点的树的最大高度不超过 lg k
Θ(1)

进一步优化——路径压缩
• 思想:每次查找的时候,如果路径较 长,则修改信息,以便下次查找的时 候速度更快
• 步骤:
– 第一步,找到根结点 – 第二步,修改查找路径上的所有节点,将
它们都指向根结点
带路径压缩的查找算法
• find3(x)
•{
• r = x;
• while (set[r] <> r) //循环结束,则找到根节点
• 这样一来,江湖上就形成了一个一个的群 落,通过两两之间的朋友关系串联起来。 而不在同一个群落的人,无论如何都无法 通过朋友关系连起来,于是就可以放心往 死了打。但是两个原本互不相识的人,如 何判断是否属于一个朋友圈呢?
• 可以在每个朋友圈内推举出一个比较有名 望的人,作为该圈子的代表人物,这样,
1 5
2 4 7 10
3 689
方法(2)——效率分析
find2(x) {
r = x; while (set[r] != r)
r = set[r]; return r; }
最坏情况Θ(N) 一般情况是…?
merge2(a, b) {
if (a<b) set[b] = a;
else set[a] = b;
的,甚至队长是谁,并不重要。所以我们可以放 任队长随意重新组队,只要不搞错敌友关系就好 了。
什么是并查集?
英文:Disjoint Set,即“不相交集合” 将编号分别为1…N的N个对象划分为不相交集合, 在每个集合中,选择其中某个元素代表所在集合。 常见两种操作: • 合并两个集合【称老大】 • 查找某元素属于哪个集合【找老大】
相关主题