vb6实现union数据结构_数据结构之并查集

动态连通性

给定一个含有N个元素的集合

  • union/合并命令:连接两个对象
  • connected/连通查询:是否有一条路径连接两个对象

53dc9dbf7d06a08e029ccaace2602700.png

连通栗子

Q:下图p和q是否连通?

52c1e35a4448e4fd7f9e942535eb8660.png

A:是的

建模对象

可归结到连通性问题的现实问题

  • 社交网络的朋友
  • 计算机网络的节点
  • 图片内的像素点

对每个对象以0-N-1进行编号

  • 使用数字作为数组的索引

建模连通关系

对连通下定义:

  • 反射性:p 连通 p
  • 对称性:如果p连通q,那么q连通p
  • 传递性:如果p连通q且q连通r,则p连通r

连通对象集合:集合内的对象相互连通

048f4b7470b7d514c487742af03f3ae5.png

操作接口定义

查找查询:查看两个对象是否属于同一个连通对象集合

合并命令:将两个连通对象集合合并为一个连通对象集合

5282a960d94328af62b46d35f97f9587.png

并查集API定义

目标:设计一个有效的数据结构

  • 对象数目N将超大
  • 对象操作M将超多
public class UF
        UF(int N)               //创建N个对象连通集合
   void union(int p,int q)      //对p,q做合并
boolean connected(int p,int q)  //对p,q做查询

快速查找算法

快速查找

数据结构

  • 一个长度为N的整形数组id[]
  • 数组值:如果id[p] == id[q],那么认为p和q连通

6451e2b79eb7396d9722140b4d33b3b3.png

查找:查看p,q是否有相同id

7940c54f7484249672176083401be887.png

合并:将所有id == id[p]的替换为id[q]

06843cf6b5f6ea16d0319dd468953645.png

JAVA实现

public class QFindUF
{
  private int[] id;
  public QFindUF(int N){
    id = new int[N];
    for(int i  = 0;i<N;i++) id[i] = i;
  }
  public boolean connected(int p,int q) { return id[p] == id[q]; }
  public void union(int p,int q){
    int pid = id[p];
    int qid = id[q];
    for(int i = 0;i<id.length;i++) if(id[i] == pid) id[i] = qid;
  }
}

快速查找太慢了

模型消耗:数组访问次数

1839e9a4d96b5f0fc4385cc2c6db8c9e.png

合并操作消耗太大:对于N个对象的N次合并,需要消耗N^2的数组操作


快速合并算法

快速合并

数据结构

  • 一个长度为N的整形数组
  • 数组值:id[i]为i的根
  • i的根为id[id[...id[i]...]]

ea19218bb8a863064cd4531865d7cfc4.png

查找:查看p和q是否有相同的根

b40e4740c5850c7dea20fa4d2516159f.png

合并:合并连通分量p和q,将p的根设置为q的根

413baa7109d679007f4d3ca8f58826a5.png

JAVA实现

public class QUnionUF{
  private int[] id;
  public QUnionUF(int N){
    id = new int[N];
    for(int i = 0;i<N;i++) id[i]=i;
  }
  
  private int root(int i){
    while(i != id[i]) i = id[i];
    return i;
  }

  public boolean connected(int p,int q) { return root(p) == root(q); }

  public void union(int p,int q){
    int pr = root(p);
    int qr = root(q);
    id[pr] = qr;
  }
}

快速合并也太慢了

模型消耗:数组访问次数

f2fe8b805ef001662af5b0eef2566bfc.png

快速查找缺点:

  • 合并操作消耗太大
  • 树太扁平,需要花费很大的资源保持树的扁平

快速合并缺点:

  • 树的深度太大
  • 查找操作消耗太大

提升操作

提升手段一:加权重

带权重并查集

  • 避免树的深度太大
  • 通过将小树挂靠到大树,来保持树的平衡因子

31d73e28b848b0d364f37e8af69c036a.png

JAVA实现

数据结构:和快速合并一样,多出一个额外数组sz[i],统计以i为根的连通分量个数

查找:和快速合并一样

return root(p) == root(q);

合并:修改快速合并的合并操作为:

  • 将小树挂靠到大树
  • 更新sz数组
int pr = root(p);
int qr = root(q);
if(pr == qr) return;
if(sz[pr] < sz[qr]) { id[pr] = qr; sz[qr] += sz[pr]; }
else                { id[qr] = pr; sz[pr] += sz[qr]; }

带权并查集分析

运行时间

  • 查找:取决于p和q的深度
  • 合并:常量时间

假设:对于每个节点x,认定深度为lgN

提升手段二:路径压缩

带路径压缩的快速合并:计算p的根时,将经过的节点的根直接指向总根

JAVA实现

只需要在循环内部增加一条语句

private int root(int i)
{
  while(i != id[i]){
    id[i] = id[id[i]];
    i = id[i];
  }
  return i;
}

实际操作:不需要将树变得完全扁平

4bde33dabbc99f238e574c7064b6b4ff.png

总结

  • WQUPC将30年运行时间缩短到6秒
  • 超级计算机帮助不会很大;good algorithm enables solution
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值