0
点赞
收藏
分享

微信扫一扫

数据结构-第七章 并查集


The Disjoint Set ADT(不相交集,并查集)

  1. 使用来表示离散中的等价类和等价关系的表示。

1. 等价类(Equivalence Class)

  1. 等价类的定义:Suppose we have a set U={1,2,…,n} of n elements and a set R={(i1,j1), (i2,j2)…(ir,jr)}of r relations. The relation R is an equivalence relation iff the following conditions are true(symbol ’≡’ represent the equivalence relation on sets, x,y,z are elements in set):(假设我们有一个n个元素组成的集合U = {1,2,…,n},一个有r个关系的集合R.当切仅当以下条件成立的时候,R才是一个等价类)
  1. Reflexive x ≡ x.(自反性)
  2. Symmetric x ≡ y,y ≡ x(对称性)
  3. Transitive x ≡ y and y ≡ z,then x ≡ z(传递性)
  1. Eg.

数据结构-第七章 并查集_并查集


数据结构-第七章 并查集_图论_02

2. 并查集提供的功能

  1. Combine(a,b):combine the equivalence classes that contains elements a and b into a single class(Combine(a,b):合并包含元素a和b的两个等价类为一个等价类)
  2. Find(e):determine the class that currently contains element e.(Find(e):找到包含元素e的等价类)

2.1. Combine(a,b)合并

  1. Combine(a,b) is equivalent to i=Find(a); j=Find(b); if(i!=j) Union(i,j);

数据结构-第七章 并查集_图论_03

3. 并查集的物理实现

  1. 并查集的物理实现是通过森林来表示。

数据结构-第七章 并查集_数据结构_04

  1. parent数组中存储的值为0的时候,这个结点表示为根结点
  2. 所以这个更快速的支持从下向上查询

//simple tree solution to union-find problem 
//使用简单的树结构解决并集的查找问题
void Initialize(int n){
    parent=new int[n+1];
    for(int e=1;e<=n;e++) parent[e]=0;
}
int Find(int e) {
    //向上找到其根结点
    while(parent[e]) e=parent[e];
    return e;
}
void Union(int i, int j) {
    //合并两个结点
    parent[j]=i;
}

3.1. Union的实现

  1. 实例:

public class DisjSets {
    public DisjSets( int numElements )
    public void union( int root1, int root2 )
    public int find( int x ) private int [] s;
}
//并查集的构造方法
public DisjSets( int numElements ) {
    s = new int [numElements];
    for( int i = 0; i < s.length; i++ )
        s[i] = -1; //一个根结点
}
//并查集的合并
public void union( int root1, int root2 ) {
    s[root2] = root1;
}
//并查集的查找,使用递归完成
public int find( int x ) {
    if( s[x] < 0 )
        return x;
    else
        return find( s[x] );
}

数据结构-第七章 并查集_结点_05

3.2. 性能估计

  1. Time complexity:(算法复杂度)
  • Find-- O(h), h 是指树高
  • Union-- θ(1)
  1. Assume that u times unions and f times finds are to be performed, f>u, in the worst case a tree with m elements can have a height of m: Union(2,1),Union(3,2),Union(4,3),Union(5,4)…(假设我们进行u次组合操作和f次查找操作,f>u,最坏情况下的一颗有m个元素的树可以有高度m)
  • 严重不平衡的树,会影响到查找的时间复杂度

数据结构-第七章 并查集_结点_06

3.3. 性能提升

3.3.1. 方法一

  1. Weight rule: if the number of nodes in tree i is less than the number in tree j, then make j the parent of i; otherwise,make i the parent of j.(点数原则:如果 i 树的点数小于 j 树的点数,那么我们让 j 成为 i 的parent,反之亦然)
  2. 结点数少的树挂到结点多的树下面

3.3.2. 高度问题的实现

数据结构-第七章 并查集_数据结构_07

  1. 为了实现我们新建一个bool类型数组来记录是否是根节点。
  2. Besides the parent field, each node has a boolean field root .The root field is true iff the node is presently a root node.The parent field of each root node is used to keep a count of the total number of nodes in the tree.(除了父字段外,每个节点都有一个布尔字段根。如果当前节点是根节点,则根字段为真。每个根节点的父字段用于统计树中的节点总数。)
  • 也就是单独使用了一个布尔数组来实现是否为根。

//Union with the weight rule
void Initialize(int n) {
    root=new bool[n+1];
    parent=new int[n+1];
    for(int e=1;e<=n;e++) {
        parent[e]=1;
        root[e]=true;
    }
}
int Find(int e) {
    while(!root[e])
        e=parent[e];
    return e;
}
void Union(int i, int j) {
    if(parent[i]<parent[j])
    //i becomes subtree of j
    {
        parent[j]=parent[j]+parent[i];
        root[i]=false;
        parent[i]=j;
    } else {
        parent[i]=parent[i]+parent[j];
        root[j]=false;
        parent[j]=i;
    }
}

  1. 如何省略去标记根的数组?
  • 使用负数来记录树高

//java
public void union( int root1, int root2 ) { 
    if(s[root2] < s[root1])
        s[root1] = root2;
    else {
        if(s[root1] == s[root2] )
            s[root1]--;
        s[root2] = root1;
    }
}//注意到负数会都反过来

数据结构-第七章 并查集_数据结构_08

  1. 例子如下

数据结构-第七章 并查集_数据结构_09

3.3.3. 方法二

  1. Height rule: if the height of tree i is less than that of tree j, then make j the parent of i; otherwise,make i the parent of j.(如果树 i 的高度小于树 j 的高度,则使 j 成为 i 的父;否则,使 i 成为 j 的父节点)
  2. 总而言之:高度低的树挂到高度高的树的下面
  3. When processing a equivalence pair, we need to operate Find twice, WeightUnion once. Example of improvement:(在处理等价对的时候,我们需要Find操作两次,WeightUnion一次。)

数据结构-第七章 并查集_并查集_10

//c++
//存在疑问?
int Find(int e) {
    /* C++ */
    int j = e;
    while(!root[j])
        j=parent[j];
    int f = e;
    while(f!=j) {
        int pf = parent[f];
        parent[f] = j;
        f = pf;
    }
}

//java是用来记录树高
public int find(int x) {
    if( s[x] < 0 )
        return x;
    else
        return s[x] = find(s[x]);
}

3.4. 性能增强

  1. improve Union in order to decrease the time each find take, so that the height of tree will not increase linearly.(改进并查集以减少每次查找所需的时间,从而使树的高度不会线性增加)
  2. Improvement of Find –path compression(查找路径压缩的改进)


举报

相关推荐

0 条评论