算法-动态连通性

  • 概念
  • 实现思路
  • 成本模型
  • quick-find算法
    • 分析
  • quick-union算法
    • 分析
    • 缺陷
  • 加权quick-union
    • 实现

概念

简单点理解就是连接两点,如果已经是连通的则忽略,比如下图:

算法-动态连通性_第1张图片

应用

  • 网络连通性:
    判断大型计算机网络中,两台计算机是否需要新连接才能通信

  • 变量名等价性:
    能判断两变量名是否等价

  • 数学集合:
    两个数是否在一个集合中

实现思路

要实现这样的算法需要下面方法:

  1. 初始化N个点,给它们标号

  2. 得到一个点所属的标号

  3. 连接两个点

  4. 判断两个点是否连通

  5. 连通分量的数量

于是得到下面的一份API:

算法-动态连通性_第2张图片

成本模型

各操作所需访问数组的次数

quick-find算法

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; iif (id[i] == pID) id[i] = qID;
    count--;
}

算法-动态连通性_第3张图片

如图,find(5,9)检查到 id[5] 和 id[9] 的标号不一样,那union操作就将5的标号1全部改成8,这样5和9的标号都一样,就全部连通了。

分析

  • 每次find()调用只需要访问数组一次

  • 每次connect() 操作调用2次find,即访问数组两次

  • 每次union()算法访问数组次数:
    2(两次find获得标号)+N(检查N个数是否有相同的标号)+1(改变1次) = N + 3
    2(两次find获得标号)+N(检查N个数是否有相同的标号)+N-1(改变N-1次) = 2*N + 1

显然,要让所有连通,则需要N-1次union,即至少(N-1)*(n+3)次访问数组,所以quick-finds是平方级别。

quick-union算法

private int find(int p)
{   // 找出分量的名称
        while (p != id[p])  p = id[p] ;
        return p;
}

public void union(int p, int q)
{   // 将p和q的根节点统一
    int pRoot = find(p);
    int qRoot = find(q);
    if (pRoot == qRoot)  return;

    id[pRoot] = qRoot;

    count--;
}

这里写图片描述

5-0 0-1 1-1 这样就找到了5的根节点1
9-8 8-8 这样就找到了9的根节点8
连接5和9只要连接它们的根节点1和8就行

分析

quick-union算法看作是quick-find的一次改良,因为它解决了quick-find的一个最重要的问题(union()操作总是线性级别的)。

缺陷

比如说下面的这种情况

这里写图片描述

可以看到这种情况下树的高度会非常高,这显然不是我们想要的结果
那怎么解决这种问题呢?

加权quick-union

总是选择将小树连接到大树就可以解决上面的问题

算法-动态连通性_第4张图片

还是那个问题,上图右边是加权的结果,可以直观的看到,加权后树的高度变为2,大大优化树的结构。

实现

要实现小树连到大树上,首先要判断小树大树,于是引进权的概念。先把所有节点的权重设为1,相连后权重相加,然后通过比较权重来判断大树小树即可。

    public WeightedQuickUnionUF(int n) {
        count = n;
        parent = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }
        public void union(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        if (rootP == rootQ) return;

        // make smaller root point to larger one
        if (size[rootP] < size[rootQ]) {
            parent[rootP] = rootQ;
            size[rootQ] += size[rootP];
        }
        else {
            parent[rootQ] = rootP;
            size[rootP] += size[rootQ];
        }
        count--;
    }

你可能感兴趣的:(读书笔记)