并查集(union-find)算法
动态连通性
、
假设程序读入一个整数对p q,如果所有已知的所有整数对都不能说明p和q是相连的,那么将这一整数对写到输出中,如果已知的数据可以说明p和q是相连的,那么程序忽略p q继续读入下一整数对.
为了实现这个效果,我们设计并查集这种数据结构来保存程序已知的所有整数对的足够多的信息,并用它们来判断一对新对象是否连通,这个问题通俗地叫做动态连通性问题.
union-find算法的api
为了方便,我们把每个对象称为触点,使用一个触点为索引的数组id[]作为基本的数据结构来表示所有分量,对于每个触点i,用find()方法来判定它分量所需的信息是否保存在id[i]中,connected()方法实现只用了一条语句 find(p) == find(q),它返回一个布尔值.
算法实现
public class UF { private int[] id; //分量id
private int count; //连通分量数目 public UF(int N){
id = new int[N];
count = N;
//初始化分量id数组
for(int i = 0; i < N; i++){
id[i] = i;
}
} //连通分量个数
public int count(){
return count;
} //是否连通
public boolean connected(int p, int q){
return find(p) == find(q);
} //在p q之间添加一条链接
public void union(int p, int q){ } //分量标识符
public int find(int p){
} public static void main(String[] args) {
// TODO Auto-generated method stub
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");
} }
quick-find算法
这种实现方法保证当且仅当 id[p] = id[q] p和q是连通的.即同一连通分量的所有触点id[]中的值全部相同,这意味着connected()只需判断id[p]和id[q]的值是否相等即可.
调用union()将p和q归并到相同的分量中,如果 id[p] == id[q],则不需要进行任何改变,否则遍历整个数组id,将所有和id[p] 相等的元素变成id[q],当然也可以将所有和id[q]相等的元素变成id[p]——两者皆可.
//在p q之间添加一条链接
public void union(int p, int q){
//p q已经连通,直接返回
if(find(p) == find(q))
return ;
//遍历数组,找出和id[p]相等的元素置换成id[q]
for(int i = 0; i < id.length; i++){
if(id[i]==find(p)){
id[i] = find(q);
}
}
count--;
} //分量标识符
public int find(int p){
return id[p];
}
find()操作很快,因为它只需要访问数组一次,但quick-find算法一般无法处理大型问题,因为对于每一次输入union()都要扫描整个id[]数组.quick-find算法的运行时间对于最终只能得到少数连通分量的一般应用是平方级的.
quick-union算法
这个算法提高了union()的速度,它和quick-find算法是互补的.
它也基于相同的数据结构——以触点作为索引的id[]数组,确切地说,每个id[]元素都是同一个分量中另一个触点的名字(也可能是自己),由它链接到另一个触点,再由这个触点链接到第三个触点,如此继续直到到达一个根触点,即链接指向自己的触点.
union(p,q)实现很简单,由p,q分别找到它们的根触点,然后将一个根触点链接到另一个.和上面一样,无论是重命名含有p的分量还是重命名含有q的分量都可以.
public int find(int p){
//找出分量名称
while(p!=id[p]){
p = id[p];
}
return p;
} public void union(int p, int q){
//将p和q的根节点统一
int pRoot,qRoot;
pRoot = find(p);
qRoot = find(p);
if(pRoot==qRoot){
return ;
}
id[pRoot] = qRoot;
count--;
}
加权quick-union算法
与其在union()中随意将一棵树连接到另一棵树,我们现在记录下每一棵树的大小并总是将较小的树连接到较大的树上.这项改动需要添加一个数组和一些代码来记录树中的节点数,它能够大大改进算法的效率,我们称它为加权quick-union算法.
public class WeightQuickUnionUF {
private int[] id; //父链接数组
private int[] sz; //(由触点索引的)各个根节点所对应分量的大小.
private int count; //连通分量数目 public WeightQuickUnionUF(int N){
id = new int[N];
count = N;
//初始化id父链接数组
for(int i = 0; i < N; i++){
id[i] = i;
}
//初始化分量大小数组
for(int i = 0; i < N; i++){
sz[i] = 1;
}
} //连通分量个数
public int count(){
return count;
} //是否连通
public boolean connected(int p, int q){
return find(p) == find(q);
} public int find(int p){
//找出分量名称
while(p!=id[p]){
p = id[p];
}
return p;
} public void union(int p, int q){
//将p和q的根节点统一
int pRoot,qRoot;
pRoot = find(p);
qRoot = find(p);
if(pRoot==qRoot){
return ;
}
if(sz[pRoot]<sz[qRoot]){
id[pRoot] = qRoot;
sz[qRoot] += sz[pRoot];
}else{
id[qRoot] = pRoot;
sz[pRoot] += sz[qRoot];
}
count--;
} }
加权quick-union算法最坏情况是将要被归并的树的大小总是相等的(且总是2的幂),这些树的结构看起来很复杂,但它们都含有2的n次方个节点,因此高度都正好是n.另外,当我们归并两个含有2的n次方个节点的树时,得到的树含有2的n+1次方个节点,此时树的高度增加到了n+1,由此推广我们可以证明quick-union算法能够保证对数级别的性能.
加权qucik-union算法是三种算法中唯一可以用于解决大型问题的算法,它在处理N个触点和M条连接时最多访问数组cMlgN次,这个结果和quick-find(以及某些情况下的quick-union算法)需要访问数组至少MN次形成了鲜明的对比.
最优算法
理想情况下,我们希望每个节点都直接链接到它的根节点上,但我们又不希望像union-find算法那样通过修改大量链接做到这一点,这时可以通过检查节点的同时把它直接链接到根节点上面去.
要实现路径压缩,只需要为find()添加一个循环,将在路径上遇到的节点全部链接到根节点.
路径压缩的加权quick-union算法是最优的算法,但并非所有操作都能在常数时间内完成.
public int find(int p){
int root = p;
//找出根节点
while(root!=id[root]){
root = id[root];
}
while(p!=root){
int x = p;
id[x] = root;
p = id[p];
}
return root;
}
各种union-find算法的性能特点
并查集(union-find)算法的更多相关文章
- 并查集(Union/Find)模板及详解
概念: 并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的Kruskal 算法和求最近公共祖先等. 操作: 并查集的基本操作有两个 ...
- 最小生成数(并查集)Kruskal算法
并查集:使用并查集可以把每个连通分量看作一个集合,该集合包含连通分量的所有点.这两两连通而具体的连通方式无关紧要,就好比集合中的元素没有先后顺序之分,只有属于和不属于的区别.#define N 100 ...
- 并查集实现Tarjan算法
本文是对http://noalgo.info/476.html的一点理解,特别是对其中 int father[mx]: //节点的父亲 int ancestor[mx]; //已访问节点集合的祖先 这 ...
- 普林斯顿算法(1.3)并查集(union-find算法)——本质就是一个数 下面的子树代表了连在一起的点
转自:https://libhappy.com/2016/03/algs-1.3/ 假设在互联网中有两台计算机需要互相通信,那么该怎么确定它们之间是否已经连接起来还是需要架设新的线路连接这两台计算机. ...
- POJ 1611 The Suspects 并查集 Union Find
本题也是个标准的并查集题解. 操作完并查集之后,就是要找和0节点在同一个集合的元素有多少. 注意这个操作,须要先找到0的父母节点.然后查找有多少个节点的额父母节点和0的父母节点同样. 这个时候须要对每 ...
- Union-Find(并查集): Quick find算法
解决dynamic connectivity的一种算法:Quick find Quick find--Data sturcture 如果两个objects是相连的,则它们有相同的array value ...
- 九度OJ 1024 畅通工程 -- 并查集、贪心算法(最小生成树)
题目地址:http://ac.jobdu.com/problem.php?pid=1024 题目描述: 省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但 ...
- HDU ACM 2586 How far away ?LCA->并查集+Tarjan(离线)算法
题意:一个村子有n个房子,他们用n-1条路连接起来,每两个房子之间的距离为w.有m次询问,每次询问房子a,b之间的距离是多少. 分析:近期公共祖先问题,建一棵树,求出每一点i到树根的距离d[i],每次 ...
- Java 并查集Union Find
对于一组数据,主要支持两种动作: union isConnected public interface UF { int getSize(); boolean isConnected(int p,in ...
- hdu5441 并查集+克鲁斯卡尔算法
这题计算 一张图上 能走的 点对有多少个 对于每个限制边权 , 对每条边排序,对每个查询排序 然后边做克鲁斯卡尔算法 的时候变计算就好了 #include <iostream> #inc ...
随机推荐
- AJAX请求详解 同步异步 GET和POST
AJAX请求详解 同步异步 GET和POST 上一篇博文(http://www.cnblogs.com/mengdd/p/4191941.html)介绍了AJAX的概念和基本使用,附有一个小例子,下面 ...
- js实现动态操作table
本章案例为通过js,动态操作table,实现在单页面进行增删改查的操作. 简要案例如下: <%@ page language="java" contentType=&quo ...
- Mysql存储过程和函数区别介绍
存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表. 存储过程和函数存在以下 ...
- SQLServer 日期函数大全
一.统计语句 1.--统计当前[>当天00点以后的数据] ) ) ORDER BY dateandtime DESC 2.--统计本周 3.--统计本月 4.统计当前 SELECT * FROM ...
- Tomcat 启动报错:No default web.xml
原因是:tomcat 中conf 目录中的 web.xml 被无意删除掉了.从其他tomcat中复制一个过来就行了. conf 目录 没有 web.xml 导致的问题是,server.xml 中配置的 ...
- linux中5条查找命令
1 which which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果. which [文件...] 参 数: -n<文件名长度> 指定文件名长 ...
- Linux下定时执行脚本(转自Decode360)
文章来自:http://www.blogjava.net/decode360/archive/2009/09/18/287743.html Decode360's Blog 老师(业精于勤而荒于嬉 ...
- PL/SQL入门理解(一)
1.PL/SQL概述 1)概念 pl/sql(procedural language)是Oracle在标准sql语言的基础上的扩展,可以实现定义变量.使用逻辑控制语句等 作用:默认oracle一次只能 ...
- windows 2003自动登录的具体步骤
在win2003系统中,使用最多的可能就是远程操作了,关于远程操作的那些事很多用户还是有些迷茫的.如果win2003系统远程重启后,要重新登录系统十分的麻烦,如何才能实现重启后的自动登录呢?让高手告诉 ...
- J2EE基础之Servlet
J2EE基础之Servlet 1. 什么是Servlet? Servlet即Java服务小程序,是使用应用程序设计接口以及相关类和方法的Java程序.它可以作为一种插件,像Applet程序一样嵌入到 ...