算法(第4版)-1.5 案例研究:union-find算法

问题→

动态连通性:当程序从输入中读取了整数对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。

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

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

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

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

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

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

时间: 2024-10-28 20:38:37

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

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

下载地址:https://download.csdn.net/download/moshenglv/10777447 算法第四版,文字版,可复制,方便copy代码 目录: 第1章 基 础 ....................... . ..........................11.1 基础编程模型 ..................................... 41.1.1 Java程序的基本结构 ................. 41.1.2原始数据类型与表达式

图灵程序设计丛书 算法(第4版)pdf

下载地址:网盘下载 内容简介  · · · · · · 本书全面讲述算法和数据结构的必备知识,具有以下几大特色. ? 算法领域的经典参考书 Sedgewick畅销著作的最新版,反映了经过几十年演化而成的算法核心知识体系 ? 内容全面 全面论述排序.搜索.图处理和字符串处理的算法和数据结构,涵盖每位程序员应知应会的50种算法 ? 全新修订的代码 全新的Java实现代码,采用模块化的编程风格,所有代码均可供读者使用 ? 与实际应用相结合 在重要的科学.工程和商业应用环境下探讨算法,给出了算法的实际代

分享《自然语言处理理论与实战》PDF及代码+唐聃+《深入浅出Python机器学习》PDF及代码+段小手+《深度学习实践:计算机视觉》PDF+缪鹏+《最优化理论与算法第2版》高清PDF+习题解答PDF+《推荐系统与深度学习》PDF及代码学习

<自然语言处理理论与实战>高清PDF,362页,带书签目录,文字可以复制:配套源代码.唐聃等著. <大数据智能互联网时代的机器学习和自然语言处理技术>PDF,293页,带书签目录,文字可以复制,彩色配图.刘知远等著.  下载: https://pan.baidu.com/s/1waP6C086-32_Lv0Du3BbNw 提取码: 1ctr <自然语言处理理论与实战>讲述自然语言处理相关学科知识和理论基础,并介绍使用这些知识的应用和工具,以及如何在实际环境中使用它们.由

机器学习入门系列02,Regression 回归:案例研究

为什么要先进行案例研究? 没有比较好的数学基础,直接接触深度学习会非常抽象,所以这里我们先通过一个预测 Pokemon Go 的 Combat Power (CP) 值的案例,打开深度学习的大门. Regression (回归) 应用举例(预测Pokemon Go 进化后的战斗力) 比如估计一只神奇宝贝进化后的 CP 值(战斗力). 下面是一只妙蛙种子,可以进化为妙蛙草,现在的CP值是14,我们想估计进化后的CP值是多少:进化需要糖果,好处就是如果它进化后CP值不满意,那就不用浪费糖果来进化它了

吴恩达《深度学习》-课后测验-第三门课 结构化机器学习项目(Structuring Machine Learning Projects)-Week2 Autonomous driving (case study) (case study)( 自动驾驶 (案例研究))

Week2 Autonomous driving (case study) (case study)( 自动驾驶 (案例研究)) \1. To help you practice strategies for machine learning, in this week we'll present another scenario and ask how you would act. We think this "simulator" of working in a machine l

深度学习数学《矩阵分析与应用第2版》资料+《最优化理论与算法第2版》电子书资料+《人工智能一种现代的方法第3版》资料学习

理性模型的建模和算法,经验模型的决策建模和算法,构成了目前人工智能的基础,基于学习反馈的模型构成了它的进化.学习人工智能,必须有较好的数学基础. <矩阵分析与应用第2版>电子书中总结了大量线性代数的知识,是一本很不错的书,数学专业可以跳过,主要是给工科生用的.归纳了不少论文中的解法,是做信号处理的一本很不错的工具书,建议认真学习,做电子笔记,对书的重点内容要好好研究. <矩阵分析与应用第2版>系统.全面地介绍矩阵分析的主要理论.具有代表性的方法及一些典型应用,包括矩阵代数基础.特殊

优化字符串的使用:案例研究

优化字符串的使用:案例研究 C++的std::string类模板是C++标准库中使用最广泛的特性之一.只要操作字符串的代码会被频繁地执行,那么就有优化的用武之地. 为什么字符串很麻烦 字符串在概念上很简单,但是想要实现高效的字符串却非常微妙.由于std::string中特性的特定组合的交互方式,使得实现高效的字符串几乎不可能. 字符串的某些行为会增加使用它们的开销,这一点与实现方式无关.字符串是动态分配的,它们在表达式中的行为与值相似,而且实现它们需要大量的复制操作. 字符串是动态分配的 字符串

对一致性Hash算法,Java代码实现的深入研究

一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法和一致性Hash算法的算法原理做了详细的解读. 算法的具体原理这里再次贴上: 先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),接着在

【JavaScript】【算法】JavaScript版排序算法

JavaScript版排序算法:冒泡排序.快速排序.插入排序.希尔排序(小数据时,希尔排序会比快排快哦) 1 //排序算法 2 window.onload = function(){ 3 var array = [0,1,2,44,4, 4 324,5,65,6,6, 5 34,4,5,6,2, 6 43,5,6,62,43, 7 5,1,4,51,56, 8 76,7,7,2,1, 9 45,4,6,7,8]; 10 //var array = [4,2,5,1,0,3]; 11 array