算法-动态连通性

概念

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

这里写图片描述

应用

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

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

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

实现思路

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

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

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

  3. 连接两个点

  4. 判断两个点是否连通

  5. 连通分量的数量

于是得到下面的一份API:

这里写图片描述

成本模型

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

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

这里写图片描述

如图,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

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

这里写图片描述

还是那个问题,上图右边是加权的结果,可以直观的看到,加权后树的高度变为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--;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值