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.5node.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 stubDisjointSet<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 verticesg.addVertex(v1);g.addVertex(v2);g.addVertex(v3);g.addVertex(v4);// add edges to create a circuitg.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.5node.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 verticesg.addVertex(v1);g.addVertex(v2);g.addVertex(v3);g.addVertex(v4);// add edges to create a circuitg.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。

转载于:https://www.cnblogs.com/zhchoutai/p/8378605.html

【算法导论-36】并查集(Disjoint Set)具体解释相关推荐

  1. C++并查集Disjoint Set(附完整源码)

    C++并查集Disjoint Set 并查集Disjoint Set算法的完整源码(定义,实现,main函数测试) 并查集Disjoint Set算法的完整源码(定义,实现,main函数测试) #in ...

  2. 基于C语言,详解Kruskal算法(利用并查集)实现构建最小生成树

    目录 一.Kruskal算法的基本介绍 具体做法:找出森林中连接任意两棵树的所有边中,具有最小权值的边,如果将它加入生成树中不产生回路,则它就是生成树中的一条边.这里的关键就是如何判断"将它 ...

  3. C++用并查集Disjoint union实现connected component连通分量(附完整源码)

    C++用并查集Disjoint union实现connected component连通分量 C++用并查集Disjoint union实现connected component连通分量完整源码(定义 ...

  4. C语言实现并查集(Disjoint set或者Union-find set)(附完整源码)

    实现实现并查集 实现并查集(Disjoint set或者Union-find set)的完整源码(定义,实现,main函数测试) 实现并查集(Disjoint set或者Union-find set) ...

  5. 【算法训练营】 - ⑩ 并查集与图

    [算法训练营] - ⑩ 并查集与图 并查集 并查集特征 并查集的优化 图 图结构的表达 图的面试题如何搞定? 图的数据结构 点 边 图 生成图 图算法 广度优先遍历 深度优先遍历 图的拓扑排序算法 最 ...

  6. 数据结构 7并查集(DISJOINT SET)

    并查集(The disjoint set ADT) 等价关系 Relation R:若对于每一对元素(a,b),a,b∈S,aRb或者为true或者为false,则称集合S上定义关系R.如果aRb为t ...

  7. 【数据结构与算法基础】并查集原理、封装实现及例题解析(C和java)

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  8. mst算法matlab_基于并查集+Kruskal算法的matlab程序及最小生成树绘图

    学了一天最小生成树,稍稍总结一下,这是第一篇 kruskal算法 关于kruskal算法已有大量的资料,不再赘述,算法流程为: 得到邻接矩阵和权值: 初始化,连接距离最小的两点: 连接距离次小的两点, ...

  9. 算法与数据结构——并查集

    文章推荐:[算法与数据结构]-- 并查集 例子: 数据结构--最小生成树之克鲁斯卡尔算法(Kruskal) 1.2 并查集思想(重点) 我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点 ...

  10. Kruskal算法:贪心+并查集=最小生成树

    http://www.51nod.com/ Kruskal算法的高效实现需要一种称作并查集的结构.我们在这里不介绍并查集,只介绍Kruskal算法的基本思想和证明,实现留在以后讨论. Kruskal算 ...

最新文章

  1. float js 正则 验证_使用HTML和Vuejs进行表单验证
  2. 数据蒋堂 | 倍增分段技术
  3. iOS学习笔记2-微博cell界面的实现
  4. 祝贺中国队夺得第十届苏迪曼杯冠军!
  5. 在Hadoop上运行基于RMM中文分词算法的MapReduce程序
  6. 第十五篇 Python之文件处理
  7. apache的日志切割
  8. Android开发1、2周——GeoQuiz项目
  9. 在VC2015里包含了lib库,但没有设置对路径的出错
  10. 425_PICkit2烧写PIC18F4580 MCU
  11. sql 2005 用户 sa 登录失败
  12. RTX3070深度学习环境配置
  13. Android——excise(用线性布局、表格布局、相对布局做发送邮件界面)
  14. nodejs使用Moment.js操作日期时间
  15. 双 JK 触发器 74LS112 逻辑功能。真值表_数电实验 | 时序逻辑电路
  16. mysqlbinlog 加-v -vv 的区别
  17. Intel 正式宣布8代酷睿处理器:14nm、性能提升15%
  18. 我的世界1.8.9作弊java_我的世界超强作弊代码大全 掌控整个世界
  19. 百度7天GNN学习-图与图学习中
  20. 公众号文章和菜单栏如何直接点击拨打客服电话号码?

热门文章

  1. 入驻商家卖违禁药物、评论造假、试水会员,直播能让新氧再次“乘风破浪”吗?
  2. linux文件解压缩加解密
  3. 云痕大数据 家长登录_云痕大数据——苏州云痕教育科技有限公司
  4. 沪江日语频道 » 日语真题
  5. 有测试狗狗好坏的软件吗,6个测试判断狗狗性格,胆小或凶猛一测便知,你家狗狗是哪种?...
  6. Substance Painter 画高度贴图
  7. 涠洲岛形成及地形地貌特征
  8. 在计算机里看不到硬盘的信息,在电脑bios里面检测不到硬盘如何解决?
  9. 437 路径总和-02
  10. 利用python爬取飞猪信息_python+selenium爬取飞猪酒店详情信息