【算法导论-36】并查集(Disjoint Set)具体解释
WiKi
Disjoint是“不相交”的意思。Disjoint Set高效地支持集合的合并(Union)和集合内元素的查找(Find)两种操作,所以Disjoint Set中文翻译为并查集。
就《算法导论》21章来讲,主要设计这几个知识点:
用并查集计算图的连通区域;
推断两个顶点是否属于同一个连通区域;
链表实现并查集;
Rooted tree实现并查集;
Rooted tree实现并查集时採用rank方法和路径压缩算法。
《算法导论》21.4给出了一个结论:总计m个MAKE-SET、UNION、FIND-SET操作。当中MAKE-SET的个数为n,则採用rank和路径压缩算法实现的并查集最坏时间复杂度是O(m α(n) )。当中α是Ackerman函数的某个反函数,这个函数的值能够看成是不大于4。所以,并查集的三种典型操作的时间复杂度是线性的。
相关资料
并查集的java实现
这里依据《算法导论》的21.3节的伪代码,实现了一个泛型的并查集。输出时,打印节点及其集合的代表元素(即根元素。representative)。
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
/**
* <p>并查集的实现<p/>
* <p>參考:《算法导论》21.3节<p/>
* <p>created by 曹艳丰<p/>
* <p>2016-08-31<p/>
*
* */
public class DisjointSet<T> {
private List<Node> forests;//全部节点
public DisjointSet(){
forests=new ArrayList<Node>();
}
/**
* 内部类,并查集的rooted node
* */
private class Node{
Node parent;
int rank;
T t;
private Node(T t){
parent=this;
rank=0;
this.t=t;
}
}
//向森林中加入节点
public void makeSet(T t){
Node node=new Node(t);
forests.add(node);
}
//将包括x和包括y的两个集合进行合并
public void union(T x,T y){
Node xNode=isContain(x);
Node yNode=isContain(y);
if (xNode!=null&&yNode!=null) {
link(findSet(xNode), findSet(yNode));
}
}
//查找到节点node的根节点
public Node findSet(Node node){
if (node!=node.parent) {
//路径压缩,參考《算法导论》插图21.5
node.parent=findSet(node.parent);
}
return node.parent;
}
//查找到节点node的根节点
public Node findSet(T t){
Node node=isContain(t);
if (node==null) {
throw new IllegalArgumentException("不含该节点!
");
}else {
return findSet(node);
}
}
//将两个根节点代表的集合进行连接
private void link(Node xNode,Node yNode){
if (xNode.rank>yNode.rank) {
yNode.parent=xNode;
}else {
xNode.parent=yNode;
if (xNode.rank==yNode.rank) {
yNode.rank+=1;
}
}
}
//森林是否包括这个节点
private Node isContain(T t){
for (Node node : forests) {
if (node.t.equals(t)) {
return node;
}
}
return null;
}
@Override
public String toString() {
// TODO Auto-generated method stub
if (forests.size()==0) {
return "并查集为空!";
}
StringBuilder builder=new StringBuilder();
for (Node node : forests) {
Node root=findSet(node);
builder.append(node.t).append("→").append(root.t);
builder.append("\n");
}
return builder.toString();
}
}
然后測试一下
public class Main{
public static void main(String[] args) {
// TODO Auto-generated method stub
DisjointSet<String> disjointSet=new DisjointSet<String>();
disjointSet.makeSet("cao");
disjointSet.makeSet("yan");
disjointSet.makeSet("feng");
disjointSet.union("cao", "yan");
disjointSet.union("cao", "feng");
System.out.println(disjointSet.toString());
}
}
输出格式,元素→代表元素
cao→yan
yan→yan
feng→yan
表明3个节点的代表元素一致。即处于一个集合中。
图的连通区域计算`
《算法导论》21.1节的伪代码。这里给出连通区域计算的样例。图的数据结构採用“【算法导论-35】图算法JGraphT开源库介绍 “中的无向图。
private static void connectedComponents(){
UndirectedGraph<String, DefaultEdge> g =
new SimpleGraph<>(DefaultEdge.class);
String v1 = "v1";
String v2 = "v2";
String v3 = "v3";
String v4 = "v4";
// add the vertices
g.addVertex(v1);
g.addVertex(v2);
g.addVertex(v3);
g.addVertex(v4);
// add edges to create a circuit
g.addEdge(v1, v2);
g.addEdge(v2, v3);
//连通区域计算
//參考《算法导论》21.1节
DisjointSet<String> disjointSet=new DisjointSet<String>();
for ( String v : g.vertexSet()) {
disjointSet.makeSet(v);
}
// for ( DefaultEdge e : g.edgeSet()) {
// String source=e.getSource();//protected訪问类型
// String target=e.getTarget();//protected訪问类型
// if (disjointSet.findSet(source)!=disjointSet.findSet(target)) {
// disjointSet.union(source, target);
// }
// }
if (disjointSet.findSet(v1)!=disjointSet.findSet(v2)) {
disjointSet.union(v1, v2);
}
if (disjointSet.findSet(v2)!=disjointSet.findSet(v3)) {
disjointSet.union(v2, v3);
}
System.out.println(disjointSet.getSetCounter());
}
输出
v1→v2
v2→v2
v3→v2
v4→v4
v1、v2、v3的代表元素一致。表明三者在一个集合中,即三者连通。
v4是另外一个集合。
实例应用
举个样例,某人结婚时宴请宾客,A来宾认识B来宾,B来宾认识C来宾,则A、B、C安排在一桌。
A来宾认识B来宾,且A、B的熟人及其熟人的熟人(熟人链)不包括C,则C与A、B不在一桌。问。须要多少桌子才干满足要求呢?
这个样例事实上就是连通区域的详细到社交关系的1度、2度……n度关系。
略微改动并查集的实例,加入集合的计数setCounter,每次makeset时递增,union时递减。这样就得到最后的集合个数。
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
/**
* <p>并查集的实现<p/>
* <p>參考:《算法导论》21.3节<p/>
* <p>created by 曹艳丰<p/>
* <p>2016-08-31<p/>
*
* */
public class DisjointSet<T> {
private List<Node> forests;//全部节点
private int setCounter;//集合计数
public DisjointSet(){
forests=new ArrayList<Node>();
setCounter=0;
}
public int getSetCounter() {
return setCounter;
}
/**
* 内部类,并查集的rooted node
* */
private class Node{
Node parent;
int rank;
T t;
private Node(T t){
parent=this;
rank=0;
this.t=t;
}
}
//向森林中加入节点
public void makeSet(T t){
Node node=new Node(t);
forests.add(node);
setCounter++;
}
//将包括x和包括y的两个集合进行合并
public void union(T x,T y){
if (x.equals(y)) {
throw new IllegalArgumentException("Union的两个元素不能相等。");
}
Node xNode=isContain(x);
Node yNode=isContain(y);
if (xNode!=null&&yNode!=null) {
link(findSet(xNode), findSet(yNode));
setCounter--;
}
}
//查找到节点node的根节点
public Node findSet(Node node){
if (node!=node.parent) {
//路径压缩,參考《算法导论》插图21.5
node.parent=findSet(node.parent);
}
return node.parent;
}
//查找到节点node的根节点
public Node findSet(T t){
Node node=isContain(t);
if (node==null) {
throw new IllegalArgumentException("不含该节点!
");
}else {
return findSet(node);
}
}
//将两个根节点代表的集合进行连接
private void link(Node xNode,Node yNode){
if (xNode.rank>yNode.rank) {
yNode.parent=xNode;
}else {
xNode.parent=yNode;
if (xNode.rank==yNode.rank) {
yNode.rank+=1;
}
}
}
//森林是否包括这个节点
private Node isContain(T t){
for (Node node : forests) {
if (node.t.equals(t)) {
return node;
}
}
return null;
}
@Override
public String toString() {
// TODO Auto-generated method stub
if (forests.size()==0) {
return "并查集为空!";
}
StringBuilder builder=new StringBuilder();
for (Node node : forests) {
Node root=findSet(node);
builder.append(node.t).append("→").append(root.t);
builder.append("\n");
}
return builder.toString();
}
}
连通区域的计算,只是这里输出的是集合个数。
private static void connectedComponents(){
UndirectedGraph<String, DefaultEdge> g =
new SimpleGraph<>(DefaultEdge.class);
String v1 = "v1";
String v2 = "v2";
String v3 = "v3";
String v4 = "v4";
// add the vertices
g.addVertex(v1);
g.addVertex(v2);
g.addVertex(v3);
g.addVertex(v4);
// add edges to create a circuit
g.addEdge(v1, v2);
g.addEdge(v2, v3);
//连通区域计算
//參考《算法导论》21.1节
DisjointSet<String> disjointSet=new DisjointSet<String>();
for ( String v : g.vertexSet()) {
disjointSet.makeSet(v);
}
// for ( DefaultEdge e : g.edgeSet()) {
// String source=e.getSource();//protected訪问类型
// String target=e.getTarget();//protected訪问类型
// if (disjointSet.findSet(source)!=disjointSet.findSet(target)) {
// disjointSet.union(source, target);
// }
// }
if (disjointSet.findSet(v1)!=disjointSet.findSet(v2)) {
disjointSet.union(v1, v2);
}
if (disjointSet.findSet(v2)!=disjointSet.findSet(v3)) {
disjointSet.union(v2, v3);
}
System.out.println(disjointSet.getSetCounter());
}
输出是2。
【算法导论-36】并查集(Disjoint Set)具体解释的更多相关文章
- 【算法与数据结构】并查集 Disjoint Set
并查集(Disjoint Set)用来判断已有的数据是否构成环. 在构造图的最小生成树(Minimum Spanning Tree)时,如果采用 Kruskal 算法,每次添加最短路径前,需要先用并查 ...
- 并查集(Disjoint Set)
在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中.这一类问题其特点是看似并不复杂, ...
- 【数据结构】【计算机视觉】并查集(disjoint set)结构介绍
1.简述 在实现多图像无序输入的拼接中,我们先使用surf算法对任意两幅图像进行特征点匹配,每对图像的匹配都有一个置信度confidence参数,来衡量两幅图匹配的可信度,当confidence> ...
- hdu 4641 K-string SAM的O(n^2)算法 以及 SAM+并查集优化
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4641 题意:有一个长度为n(n < 5e4)的字符串,Q(Q<=2e5)次操作:操作分为:在末 ...
- hdu 1233(还是畅通project)(prime算法,克鲁斯卡尔算法)(并查集,最小生成树)
还是畅通project Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Tota ...
- POJ 2421 Constructing Roads (Kruskal算法+压缩路径并查集 )
Constructing Roads Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 19884 Accepted: 83 ...
- hdu 4641K-string SAM的O(n^2)算法 以及 SAM+并查集优化
转载:http://www.cnblogs.com/hxer/p/5675149.html 题意:有一个长度为n(n < 5e4)的字符串,Q(Q<=2e5)次操作:操作分为:在末尾插入一 ...
- 距离LCA离线算法Tarjan + dfs + 并查集
距离B - Distance in the Tree 还是普通的LCA但是要求的是两个节点之间的距离,学到了一些 一开始我想用带权并查集进行优化,但是LCA合并的过程晚于离线计算的过程,所以路径长度会 ...
- 并查集(Disjoint Set Union,DSU)
定义: 并查集是一种用来管理元素分组情况的数据结构. 作用: 查询元素a和元素b是否属于同一组 合并元素a和元素b所在的组 优化方法: 1.路径压缩 2.添加高度属性 拓展延伸: 分组并查集 带权并查 ...
随机推荐
- 【Redis发布订阅】
Redis通过PUBLISH.SUBSCRIBE等命令实现发布与订阅模式. 举例:QQ群的公告,单个发布者,多个收听着. *** 发布/订阅 PUBLISH 频道 消息 将消息发布到指定的频道. . ...
- Yeslab 华为安全HCIE-第七门-Agile Controlle
课程目录: 华为安全HCIE-第七门-Agile Controller(12篇)\1_aglie_controller产品亮点讲解.avi 华为安全HCIE-第七门-Agile Controlle ...
- wordcontent小结
gitee地址: https://gitee.com/yzpdegit/test 问题描述: 计算一个文件中所包含的单词数,字符个数,行数 需求分析: WordCount的需求可以概括为:对程序设计语 ...
- which---查找并显示给定命令的绝对路径
which命令用于查找并显示给定命令的绝对路径,环境变量PATH中保存了查找命令时需要遍历的目录.which指令会在环境变量$PATH设置的目录里查找符合条件的文件.也就是说,使用which命令,就可 ...
- 00076_BigDecimal
1.在程序中执行下列代码,会出现什么问题? System.out.println(0.09 + 0.01); System.out.println(1.0 - 0.32); System.out.pr ...
- hdparm
https://www.douban.com/note/244813504/ http://blog.sina.com.cn/s/blog_413d250e0101jtr7.html http://m ...
- Junit4.x高级使用方法具体解释(一)
近期整理代码的时候,总习惯把一些经常使用的工具类和方法等都写在junit中,这样能够方便于在想用的时候直接copy,在用junit的时候学到了一些比較实用的东西.记录例如以下: 1.使用junit进行 ...
- hdu5024
思路要开阔些,或者说要转化一下思路,别太死 把每一个点当拐点,爆一边就能够.用记忆化搜索也行.都不会超时 #include<bits/stdc++.h> using namespace s ...
- [Codeforces558E]A Simple Task 线段树
链接 题意:给定一个长度不超过 \(10^5\) 的字符串(小写英文字母),和不超过5000个操作. 每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序. 最后输出最终 ...
- Qwt库的一个使用注意事项
作者:朱金灿 来源:http://blog.csdn.net/clever101 一般debug版本的程序链接release版本的库是没有问题的.今天使用debug版本程序链接release版本的qw ...