问题→

动态连通性:当程序从输入中读取了整数对p q时,如果已知的所有整数对都不能说明p和q是相连的,那么则将这一对整数写入到输出中。如果已知的数据可以说明p和q

是相连的,那么程序应该忽略p q这对整数并继续处理输入中的下一对整数。

该问题的应用→

网络,变量名等价性,数字集合等。

设计API→

public class UF
  UF(int N) 以整数标识(0到N-1)初始化N个触点
void union(int p, int q) 在p和q之间添加一条连接
int find(int p) p(0到N-1)所在的分量的标识符
boolean connected(int p, int q) 如果p和q存在于同一个分量中则返回true
int count() 连通分量的数量

为解决动态连通性问题设计算法的任务转化为了实现这份API。

quick-find算法→

public class QuickFindUF {

    private int[] id;     // 分量id(以触点作为索引)
private int count; // 分量数量 public QuickFindUF(int N) { // 初始化分量id数组
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);
} public int find(int p) {
return id[p];
} public void union(int p, int q) { // 将p和q归并到相同的分量中
int pID = find(p);
int qID = find(q); // 如果p和q已经在相同的分量之中则不需要采取任何行动
if (pID == qID) return; // 将p的分量重命名为q的名称
for (int i = 0; i < id.length; i++) {
if (id[i] == pID) id[i] = qID;
} count--;
} public static void main(String[] args) { // 解决由StdIn得到的动态连通性问题
int N = StdIn.readInt(); // 读取触点数量
UF uf = new UF(N); // 初始化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");
} }

QuickFindUF

分析:

· 每次find()调用只需要访问数组一次,而归并两个分量的union()操作访问数组的次数在(N + 3)到(2N + 1)之间。

· 假设最后只得到了一个连通分量,那么这至少需要调用N - 1次union(),即至少(N + 3)(N - 1) ~ N ^ 2次数组访问。

· quick-find算法是平方级别的。

quick-union算法→

public class QuickUnionUF {

    private int[] id;     // 分量id(以触点作为索引)
private int count; // 分量数量 public QuickUnionUF(int N) { // 初始化分量id数组
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);
} public int find(int p) {
// return id[p];
while (p != id[p]) p = id[p];
return p;
} public void union(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) return; id[pRoot] = qRoot; count--;
} public static void main(String[] args) { // 解决由StdIn得到的动态连通性问题
int N = StdIn.readInt(); // 读取触点数量
UF uf = new UF(N); // 初始化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");
} }

QuickUnionUF

分析:

· 最佳情况的输入,用例的运行时间是线性级别的。

· 最坏情况的输入,用例的运行时间是平方级别的。

加权quick-union算法→

public class WeightedQuickUnionUF {
private int[] id;
private int[] sz;
private int count; public WeightedQuickUnionUF(int N) {
count = N;
id = new int[N];
for (int i = 0; i < N; i++) id[i] = i;
sz = new int[N];
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) {
int i = find(p);
int j = find(q);
if (i == j) return;
// 将小树的根节点连接到大树的根节点
if (sz[i] < sz[j]) {
id[i] = j;
sz[j] += sz[i];
} else {
id[j] = i;
sz[i] += sz[j];
}
count--;
} public static void main(String[] args) { // 解决由StdIn得到的动态连通性问题
int N = StdIn.readInt(); // 读取触点数量
UF uf = new UF(N); // 初始化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");
} }

WeightedQuickUnionUF

分析:

· 记录每一棵树的大小并总是将较小的树连接到较大的树上。

· 加权quick-union算法是对数级别的。

最优算法→

路径压缩的加权quick-union算法是最优的算法,但并非所有的操作都能在常数时间内完成。

均摊成本的图像→

· 对于quick-find算法:累计平均值一开始较高,后来开始下降,但仍保持了相对较高的水平。

· 对于quick-union算法:累计平均值在初始阶段较低,后期增长很明显。

· 对于加权quick-union算法:没有任何昂贵的操作,均摊成本也很低。

展望→

讨论问题时的基本步骤:

· 完整而详细地定义问题,找出解决问题所必需的基本抽象操作并定义一份API。

· 简洁地实现一种初级算法,给出一个精心组织的开发用例并使用实际数据作为输入。

· 当实现所能解决的问题的最大规模达不到期望时决定改进还是放弃。

· 逐步改进实现,通过经验性分析或(和)数学分析验证改进后的效果。

· 用更高层次的抽象表示数据结构或算法来设计更高级的改进版本。

· 如果可能尽量为最坏情况下的性能提供保证,但在处理普通数据时也要有良好的性能。

· 在适当的时候将更细致的深入研究留给有经验的研究者并继续解决下一个问题。

算法(第4版)-1.5 案例研究:union-find算法的更多相关文章

  1. 《算法 (第4版)》【PDF】下载

    <算法 (第4版)>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196349 (第4版)>[PDF]"  TITL ...

  2. 算法第四版-文字版-下载地址-Robert Sedgewick

    下载地址:https://download.csdn.net/download/moshenglv/10777447 算法第四版,文字版,可复制,方便copy代码 目录: 第1章 基 础 ...... ...

  3. 【原创】数据挖掘案例——ReliefF和K-means算法的医学应用

    数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘 (DataMiriing),指的是从大型数据库或数据仓库中提取人们感兴趣的知识,这些知识是隐含的.事先未知 ...

  4. Top100Summit全球案例研究峰会第一天总结——云计算和大数据

    很荣幸受邀参加Top100Summit全球软件案例研究峰会,这次的大会主题是<技术推动商业变革>,组委会从全国投稿的460多件案例中甄选出100件具有代表价值的案例,进行为期4天的分享,第 ...

  5. 十大经典排序算法的JS版

    前言 个人博客:Damonare的个人博客 如遇到问题或有更好的优化方法,可以: 提issue给我 或是pull requests 我都会看到并处理,欢迎Star. 这世界上总存在着那么一些看似相似但 ...

  6. Java虚拟机—垃圾回收算法(整理版)

    1.概述 由于垃圾收集算法的实现涉及大量的程序细节.因此本节不打算过多地讨论算法的实现,只是介绍几种算法的思想及其发展过程.主要涉及的算法有标记-清除算法.复制算法.标记-整理算法.分代收集算法. 2 ...

  7. Netty实战十四之案例研究(一)

    1.Droplr——构建移动服务 Bruno de Carvalho,首席架构师 在Droplr,我们在我的基础设施的核心部分.从我们的API服务器到辅助服务的各个部分都使用了Netty. 这是一个关 ...

  8. C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】

    C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...

  9. 洞察行业领先者的前沿思想——第五届TOP100全球软件案例研究峰会精彩谢幕

    (第五届TOP100summit开幕式现场) 12月09日-12日,由msup主办的第五届TOP100全球软件案例研究峰会(以下简称TOP100summit)在北京国家会议中心举行,作为互联网行业最有 ...

随机推荐

  1. 常用备份工具是mysql自带的mysqldump

    常用备份工具是mysql自带的mysqldump,mysqldump -u root -p密码 dbname >d:\test.sql ------------备份某个库mysqldump -u ...

  2. C指针的大小

    应该说这是一个非常基础的问题,教科书上说指针大小和机器字长相同,即32位机指针长度为4字节!但是对不对呢?为什么是这样? 搜了一下相关资料...居然发现回答不统一,很多人也同样是糊里糊涂. 下面对这个 ...

  3. Tomcat version 7.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 and 6 Web modules

    解决方案: 找到如下文件 将"jst.web"的version改低一些

  4. 我们应当怎样学习HTML和CSS

    目标读者:web前端小白.大神请绕路 学习一门新技术,应当找一本经典入门书,在两三天之内快速翻阅完毕,了解其概貌. 然后再制定一个学习路线图(这个路线图绝大多数情况下非书本目录的顺序),接着遵循学习路 ...

  5. 使用MLeaksFinder检测项目内存泄露总结

    前几天看到开源工具MLeaksFinder,决定用在公司的项目中试一下,效果很不错,用法也很简单,直接把项目文件夹拖到项目中就可以了,依靠这个项目,我发现公司项目中有不少内存泄露的地方,在此总结一下: ...

  6. dstoon系统中学习

    $r = $db->get_one("SELECT * FROM {$DT_PRE}company WHERE username='$pay_user'");注意:usern ...

  7. C# in depth学习(1)

    第一章,C#开发的进化史 1.简单数据类型 2.排序 Sorting an ArrayList using IComparer (C# 1) Sorting a List<Product> ...

  8. maven私服搭建(centOS6.5)

    maven的好处和私服的应用本文不赘述,私服搭建如下: MAVEN 私服搭建(centOS 6.5 环境) 1.  准备环境,搭建centOS6.5系统环境,略 2.  准备对应的软件包如下: A. ...

  9. css实现强制不换行/自动换行/强制换行

    在我们日常的编码中经常会遇到这段文字不可以换行,或者自动换行的需求.虽然这个功能在我们平时很常见但是我相信大家一定不会可以的去记住它吧(至少小月是很懒惰的从来是用什么查什么 ♦ 嘻嘻...).今天我们 ...

  10. Android6.0中的权限

    Android6.0相比之前的Android版本有一个很大的不同点,就是动态的获取权限.之前我们需要什么权限只需要在Manifest文件中声明即可,在6.0中,又新增了运行时权限的动态检测. Andr ...