解决问题

给一系列对点0~N-1的连接,判断某两个点p与q是否相连。

private int[] id;

// 判断p和q是否属于同一个连通分量
public boolean connected(int p, int q) // 连接两个点
public void union(int p, int q)

  

Quick-find

connected(p, q):判断p 和 q 的id值是否相同

union(p, q): 将与p 的id 相同的所有点都改为q的id

缺点:union太慢,需要遍历id数组

Quick-union

connected(p, q):判断p 和 q 的根的id值是否相同

union(p, q): 将与p 的根的 id 改为q的根的 id

本质上是将并查集之间的关系看做一棵树

缺点:最坏情况下仍然需要遍历数组

Weighted Quick-union

connected(p, q):判断p 和 q 的根的id值是否相同

union(p, q): 判断p和q所在的树哪个大(包含的节点多),将较小的树根的 id 改为较大的树根的 id

某个节点高度增加1,当且仅当它在一颗小树T1上且被union并入大树T2中,生成的树节点数大于T1的两倍,所以某个节点的高度最多只能是lg(N)

Weighted Quick-union with Path Compression

connected(p, q):判断p 和 q 的根的id值是否相同

union(p, q): 判断p和q所在的树哪个大(包含的节点多),将较小的节点到较小的树根这条路径上所有节点的 id 改为较大的树根的 id

总结

四种方法复杂度如下,其中lg* 表示需要取对数多少次才能将N的值变为≤1,WQUPC复杂度是由论文中所得,lg*可以视为常数复杂度。

algorithm 初始化 union connected
quick find N N 1
quick union N N N
weighted quick union N lg N lg N
weighted quick union with path compression N lg* N lg* N

实现

public class UF {

    private int[] parent;  // parent[i] = parent of i
private byte[] rank; // rank[i] = rank of subtree rooted at i (never more than 31) 记录的是树的高度
private int count; // number of components /**
* Initializes an empty union-find data structure with <tt>N</tt> sites
* <tt>0</tt> through <tt>N-1</tt>. Each site is initially in its own
* component.
*
* @param N the number of sites
* @throws IllegalArgumentException if <tt>N < 0</tt>
*/
public UF(int N) {
if (N < 0) throw new IllegalArgumentException();
count = N;
parent = new int[N];
rank = new byte[N];
for (int i = 0; i < N; i++) {
parent[i] = i;
rank[i] = 0;
}
} /**
* Returns the component identifier for the component containing site <tt>p</tt>.
*
* @param p the integer representing one site
* @return the component identifier for the component containing site <tt>p</tt>
* @throws IndexOutOfBoundsException unless <tt>0 ≤ p < N</tt>
*/
public int find(int p) {
validate(p);
while (p != parent[p]) {
parent[p] = parent[parent[p]]; // path compression by halving 【完成路径压缩】
p = parent[p];
}
return p;
} /**
* Returns the number of components.
*
* @return the number of components (between <tt>1</tt> and <tt>N</tt>)
*/
public int count() {
return count;
} /**
* Returns true if the the two sites are in the same component.
*
* @param p the integer representing one site
* @param q the integer representing the other site
* @return <tt>true</tt> if the two sites <tt>p</tt> and <tt>q</tt> are in the same component;
* <tt>false</tt> otherwise
* @throws IndexOutOfBoundsException unless
* both <tt>0 ≤ p < N</tt> and <tt>0 ≤ q < N</tt>
*/
public boolean connected(int p, int q) {
return find(p) == find(q);
} /**
* Merges the component containing site <tt>p</tt> with the
* the component containing site <tt>q</tt>.
*
* @param p the integer representing one site
* @param q the integer representing the other site
* @throws IndexOutOfBoundsException unless
* both <tt>0 ≤ p < N</tt> and <tt>0 ≤ q < N</tt>
*/
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ) return; // make root of smaller rank point to root of larger rank
if (rank[rootP] < rank[rootQ]) parent[rootP] = rootQ;
else if (rank[rootP] > rank[rootQ]) parent[rootQ] = rootP;
else {
parent[rootQ] = rootP;
rank[rootP]++; //【只有此处才增加联通分量的rank】
}
count--;
} // validate that p is a valid index
private void validate(int p) {
int N = parent.length;
if (p < 0 || p >= N) {
throw new IndexOutOfBoundsException("index " + p + " is not between 0 and " + (N-1));
}
} /**
* Reads in a an integer <tt>N</tt> and a sequence of pairs of integers
* (between <tt>0</tt> and <tt>N-1</tt>) from standard input, where each integer
* in the pair represents some site;
* if the sites are in different components, merge the two components
* and print the pair to standard output.
*/
public static void main(String[] args) {
int N = StdIn.readInt();
UF uf = new UF(N);
while (!StdIn.isEmpty()) {
int p = StdIn.readInt();
int q = StdIn.readInt();
if (uf.connected(p, q)) continue;
uf.union(p, q);
StdOut.println(p + " " + q);
}
StdOut.println(uf.count() + " components");
}
}

  

Union-find 并查集的更多相关文章

  1. <算法><Union Find并查集>

    Intro 想象这样的应用场景:给定一些点,随着程序输入,不断地添加点之间的连通关系(边),整个图的连通关系也在变化.这时候我们如何维护整个图的连通性(即判断任意两个点之间的连通性)呢? 一个比较简单 ...

  2. 并查集(Union/Find)模板及详解

    概念: 并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的Kruskal 算法和求最近公共祖先等. 操作: 并查集的基本操作有两个 ...

  3. 并查集(Union Find)的基本实现

    概念 并查集是一种树形的数据结构,用来处理一些不交集的合并及查询问题.主要有两个操作: find:确定元素属于哪一个子集. union:将两个子集合并成同一个集合. 所以并查集能够解决网络中节点的连通 ...

  4. 并查集 (Union Find ) P - The Suspects

    Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized ...

  5. 并查集(Disjoint Set Union,DSU)

    定义: 并查集是一种用来管理元素分组情况的数据结构. 作用: 查询元素a和元素b是否属于同一组 合并元素a和元素b所在的组 优化方法: 1.路径压缩 2.添加高度属性 拓展延伸: 分组并查集 带权并查 ...

  6. 第三十一篇 玩转数据结构——并查集(Union Find)

    1.. 并查集的应用场景 查看"网络"中节点的连接状态,这里的网络是广义上的网络 数学中的集合类的实现   2.. 并查集所支持的操作 对于一组数据,并查集主要支持两种操作:合并两 ...

  7. 并查集(不相交集)的Union操作

    在并查集(不相交集)中附加操作\(Deunion\),它实现的功能是取消最后一次\(Union\)的操作. 实现思想 初始化一个空栈,将每一次的\(Union\)操作的两个集合的根和其值\(Push\ ...

  8. Mutual Training for Wannafly Union #6 E - Summer Trip(并查集)

    题目链接:http://www.spoj.com/problems/IAPCR2F/en/ 题目大意: 给m个数字代表的大小,之后n组数据,两两关联,关联后的所有数字为一组,从小到大输出组数以及对应的 ...

  9. POJ 1611 The Suspects 并查集 Union Find

    本题也是个标准的并查集题解. 操作完并查集之后,就是要找和0节点在同一个集合的元素有多少. 注意这个操作,须要先找到0的父母节点.然后查找有多少个节点的额父母节点和0的父母节点同样. 这个时候须要对每 ...

  10. 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

    图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...

随机推荐

  1. 构建高性能插件式Web框架

    基于MVC插件模式构建支持数据库集群.数据实时同步.数据发布与订阅的Web框架系统.如下图: 1.基于插件式开发 采用插件模式开发的优点是使得系统框架和业务模式有效地进行分离,系统更新也比较简单,只需 ...

  2. BZOJ2976:[POI2002]出圈游戏(exCRT)

    Description 有编号从1到n的n个小朋友在玩一种出圈的游戏,编号为i+1的小朋友站在编号为i小朋友左边.编号为1的小朋友站在编号为n的小朋友左边.首先编号为1的小朋友开始报数,接着站在左边的 ...

  3. PythonTip(1)

    发现一个Python的题库,嘿嘿,练练手吧~~~ http://www.pythontip.com/ a + b 描述: 给你两个数a.b,请你计算它们的和,并输出. 例如: a = 3, b = 2 ...

  4. python nmap模块 端口探测

    今天添加端口探测功能,主要实现方式是通过nmap模块调用,扫描1-65535端口.上一篇中已经将UP的PC机全部获取到,这里直接从已知在线的PC中进行端口扫描就可以了,会节省很多时间. 代码如下,还是 ...

  5. c++一些总结

    1.if和else if后面并没有要求一定要接else(即以else来结尾),可以直接if语句然后接其他语句,也可以if语句之后加else if语句再接其他语句

  6. lucene&solr学习——solr学习(二) Solr管理索引库

    1.什么是solrJ solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,SolrJ通常在嵌入在业务系统中,通过SolrJ的API接口操作Solr服务,如下图: 依赖jar包: 2 ...

  7. html、css和js原生写一个模态弹出框,顺便解决父元素半透明子元素不透明效果

    模态框: html部分: <!-- 按钮 --> <button id="box" onclick="pop_box()">弹出框< ...

  8. oracle 分组函数、视图

    组函数 分组函数作用于一组数据,对每一组返回一个值 组函数类型: 1.计数        count(列名 或 表达式)     对满足的行数进行统计 2.求和        sum(列名 或 表达式 ...

  9. 你不知道的javaScript笔记(6)

    语法 语句表达式 句子是完整表达某个意思的一组词,由一个或多个短语组成,他们之间由标点符号或者连接词连接起来. 语句相当于句子,表达式相当于短语,运算符则相当于标点符号和连接词. JavaScript ...

  10. zepto 基础知识(2)

    20.append append(content) 类型:self 在每个匹配的元素末尾插入内容(内部插入).内容可以为html 字符串.dom节点,或者节点组成的数组. $('ul').append ...