1.背景

《算法》一书中提到了关于算法的一些基本思想

  • 优秀的算法因为能够解决实际的问题而变得更为重要;
  • 高效算法的代码可以很简单;
  • 理解某个实现的性能特点是一项有趣而令人满足的挑战;
  • 在解决同一个问题的多种算法之间进行选择时,科学方法是一项重要工具;
  • 迭代式改进能够让算法效率越来越高;

使用union-find算法解决连通性问题,所谓连通性问题就是在下图网络中可以看到有很多节点,节点与节点之间的连接对称为连通分量,要求编写程序判断网络中有多少组连通分量,随意给出两个节点要求判断这两个节点是否属于同一个分量。在连通图中有如下一些定义,假如一个节点p没有和任何其他节点相连,则此节点属于一个连通分量,如果一个节点p和节点q相连,则p-q为一个连通分量,如果p和q相连,q和r相连,则p-q-r为一个连通分量。

2.算法分析

……

3.算法实现

为了解决此问题,需要先设计一个API来封装所需要的基本操作:初始化,连接两个节点,判断包含某个节点的分量,判断两个节点是否属于同一个分量之中,并且返回所有分量的个数。

public class UF
UF(int N) 整数标示N个节点
void union(int p, int q) 在pq之间添加一条连线,标示连接pq
int find(int p) 节点p所在分量的标示符
Boolean connected(int p, int q) 如果p和q存在于同一个分量中则返回true
int count() 连通分量的个数

代码实现一:

import java.util.Scanner;
public class UF {
private int[] id; //分量的id
private int count; //分量的数量 //初始化分量id数组
public UF(int N) {
count = N;
id = new int[N];
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所属的分量id
public int find(int p) {
return id[p];
} //连接节点pq
//首先检查pq是否在同一个分量中,如果是则不做任何操作,否则要求p所在的连通分量中
//所有节点id必须相同,q所在的连通分量中所有节点id也相同但为另外的值,要将二者合
//二为一,则将q所在分量的所有节点id均变为p节点的id或相反
public void union(int p, int q) {
int pId = find(p);
int qId = find(q); if (pId == qId) {
return;
} for (int i = 0; i < id.length; i++) {
if (id[i] == qId) {
id[i] = pId;
}
}
count--;
} public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
UF uf = new UF(N); while (scanner.hasNext()) {
int p = scanner.nextInt();
int q = scanner.nextInt();
if (uf.connected(p, q)) {
continue;
}
uf.union(p, q);
System.out.println(p + "--" + q);
}
System.out.println(uf.count() + " components");
}
}

使用这种find和union实现可以看到,对于find操作非常快,但对于union每一对节点输入都可能需要进行对整个id数组进行遍历,对于id数组的访问时间为O(N^2),所以当输入节点很多时用这个算法来统计连通分量就不行了,此时union的时间复杂度为O(N),find的复杂度为O(1)。

代码实现二:

public int find2(int p) {
while (p != id[p]) {
p = id[p];
}
return p;
} public void union2(int p, int q) {
int pId = find2(p);
int qId = find2(q); if (pId == qId) {
return;
} id[qId] = pId;
count--;
}

使用这种find2和union2实现可以看到,对于调用到find2时,在某些情况下对于id数组访问的时间复杂度依然为O(N^2),此时find2的时间复杂度为树的高度N,union2的复杂度也为树的高度N。

代码实现三:

public class WeightedQuickUF {
private int[] id; //节点的索引
private int[] w; //每个根节点对应分量的大小
private int count; public WeightedQuickUF(int N) {
count = N;
id = new int[N];
w = new int[N];
for (int i = 0; i < N; i++) {
id[i] = i;
w[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) {
int pId = find(p);
int qId = find(q); if(pId == qId) {
return;
} if (w[pId] < w[qId]) {
id[pId] = qId;
w[qId] += w[pId];
} else {
id[qId] = pId;
w[pId] += w[qId];
}
count--;
}
}

对第二种实现做一些改进,使用加权的算法,使用加权算法保证在使用union时总是将小树的根节点连接到大树上,此时find和union的复杂度均为树的高度lgN,所以访问id数组的复杂度最坏情况为cMlgN,此时C为常数,M为连接数,N为节点数。

union-find算法的更多相关文章

  1. 算法与数据结构基础 - 合并查找(Union Find)

    Union Find算法基础 Union Find算法用于处理集合的合并和查询问题,其定义了两个用于并查集的操作: Find: 确定元素属于哪一个子集,或判断两个元素是否属于同一子集 Union: 将 ...

  2. LeetCode编程训练 - 合并查找(Union Find)

    Union Find算法基础 Union Find算法用于处理集合的合并和查询问题,其定义了两个用于并查集的操作: Find: 确定元素属于哪一个子集,或判断两个元素是否属于同一子集 Union: 将 ...

  3. 最小生成树——Kruskal(克鲁斯卡尔)算法

    [0]README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解 Kruskal(克鲁斯卡尔)算法 的idea 并用 源代码加以实现: 0.2)最小生成树的基础知识,参见 ...

  4. Kruscal算法求图的最小生成树

    Kruscal算法求图的最小生成树 概述   和Prim算法求图的最小生成树一样,Kruscal算法求最小生成树也用到了贪心的思想,只不过前者是贪心地选择点,后者是贪心地选择边.而且在算法的实现中,我 ...

  5. MySQL 优化之 index merge(索引合并)

    深入理解 index merge 是使用索引进行优化的重要基础之一.理解了 index merge 技术,我们才知道应该如何在表上建立索引. 1. 为什么会有index merge 我们的 where ...

  6. MYSQL-联合索引

    深入理解 index merge 是使用索引进行优化的重要基础之一.理解了 index merge 技术,我们才知道应该如何在表上建立索引. 1. 为什么会有index merge 我们的 where ...

  7. MySQL index merge

    深入理解 index merge 是使用索引进行优化的重要基础之一. [ index merge]       当where谓词中存在多个条件(或者join)涉及到多个字段,它们之间进行 AND 或者 ...

  8. MySQL 查询优化之 Index Merge

    MySQL 查询优化之 Index Merge Index Merge Intersection 访问算法 Index Merge Union 访问算法 Index Merge Sort-Union ...

  9. MySQL 优化之 index_merge (索引合并)

    深入理解 index merge 是使用索引进行优化的重要基础之一.理解了 index merge 技术,我们才知道应该如何在表上建立索引. 1. 为什么会有index merge 我们的 where ...

  10. HNCU1323:算法2-1:集合union (线性表)

    http://hncu.acmclub.com/index.php?app=problem_title&id=111&problem_id=1323 题目描述 假设利用两个线性表LA和 ...

随机推荐

  1. Linux command line exercises for NGS data processing

    by Umer Zeeshan Ijaz The purpose of this tutorial is to introduce students to the frequently used to ...

  2. React Native API之 ActionSheetIOS

    ok!先来演示是下效果: 官网教程:http://reactnative.cn/docs/0.31/actionsheetios.html#content 上代码:引入API组件: import Re ...

  3. 【NOI2013】树的计数

    Description 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的DFS序以及BFS序.两棵不同的树的DFS序有可能相同,并且它们的BFS序也有可能相同, ...

  4. Python写ROS 订阅与发布程序

    1. 编写talker代码 vim ..../src/talker.py #!/usr/bin/env python # license removed for brevity import rosp ...

  5. django QueryDict对象

    类的原型:class QueryDict[source] 在HttpRequest对象中,GET和POST属性都是一个django.http.QueryDict的实例.也就是说你可以按本文下面提供的方 ...

  6. vs下手敲git命令补遗

    大多数常用的点按钮就行了.少数还是手敲方便 ng new myapp   的时候已经新建git了,不过只有master分支. 创建新分支可以点vs左下角分支名字,然后在上方弹出的命令行里写名字,不过鼠 ...

  7. 开发环境运行正常,发布服务器后提示HTTP 错误 403.14 - Forbidden

    一.发布服务器后报错 今天在项目发布中遇到一件奇怪的事,开发完成的项目,发布到服务器上时 1. 发布到A服务器,一切正常 2. 发布到B服务器,提示403服务器错误 在同事电脑上重新打包发布代码,并发 ...

  8. CentOS 7 install Nginx

    1. rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.r ...

  9. python-day8-元组的内置方法

    #为何要有元组,存放多个值,元组不可变,更多的是用来做查询# t=(1,[1,3],'sss',(1,2)) #t=tuple((1,[1,3],'sss',(1,2)))# print(type(t ...

  10. 原创-整理了下常用的js数组 、对象、数字、字符串的操作方法

    终于整理好了...主要是一些常用的函数,包含es6和es5的所有常用的,吧一些不常用的全部砍掉,省的大家看的费事.发现这个到博客上面有点乱.给个百度云地址:https://pan.baidu.com/ ...