算法 {并查集}

算法 {并查集}

并查集

性质

并查集与FLoyd的关系: @LINK: (https://editor.csdn.net/md/?articleId=134838290)-(@LOC_0);

算法

樸素版本

class ___DisjointSet{
public:
    vector<int> __Ancester;

    void Initialize( int _range){
        __Ancester.resize(_range);  ::std::iota( __Ancester.begin(), __Ancester.end(), 0);
    }
    int GetRoot( int _a){
        ASSERT_SYSTEM_( _a>=0 && _a<int(__Ancester.size()));
        function<int(int)> dfs = [&]( int _cur){
            if( __Ancester[ _cur] == _cur){
                return _cur;
            }
            __Ancester[ _cur] = dfs( __Ancester[ _cur]);
            return __Ancester[ _cur];
        };
        return dfs( _a);
    }
    void Merge( int _a, int _b){
        ASSERT_SYSTEM_( _a>=0 && _a<int(__Ancester.size()) && _b>=0 && _b<int(__Ancester.size()));
        __Ancester[ GetRoot(_a)] = GetRoot(_b);
    }
    void Debug(){
        cout<< "___DisjointSet-Debug-Begin\n";
        map<int,vector<int> > Mp;
        for( int i = 0; i < int(__Ancester.size()); ++i){
            Mp[GetRoot(i)].push_back( i);
        }
        for( auto & i : Mp){
            cout<< "Root("<< i.first<< "): {"; for( auto j = i.second.begin(); j != i.second.end(); ++j){ if(j!=i.second.begin()){cout<<",";} cout<< *j;} cout<< "}\n";
        }
        cout<< "___DisjointSet-Debug-End\n";
    }
};
//} ___DisjointSet

维护深度Depth

代碼
class ___DisjointSet_withDepth{
public:
    vector<int> __Ancester;
    vector<int64_t> __Depth;
    vector<int> __Size;
    int __Range;

    void Initialize( int _range){
        __Range = _range;
        __Ancester.resize(__Range);  std::iota( __Ancester.begin(), __Ancester.end(), 0);
        __Size.assign( __Range, 1);
        __Depth.resize( __Range);  memset( __Depth.data(), 0, sizeof( __Depth[0]) * __Depth.size());
    }
    int GetRoot( int _a){
        ASSERT_WEAK_( _a>=0 && _a<__Range);
        function<int(int)> dfs = [&]( int _cur){
            if( __Ancester[ _cur] == _cur){
                return _cur;
            }
            int fa = __Ancester[ _cur];
            __Ancester[ _cur] = dfs( __Ancester[ _cur]);
            __Depth[ _cur] += __Depth[ fa];
            return __Ancester[ _cur];
        };
        return dfs( _a);
    }
    void MergeTo( int _fa, int _fb, int64_t _depth){
        ASSERT_( _fa!=_fb && _fa==GetRoot(_fa) && _fb==GetRoot(_fb));
        __Ancester[ _fa] = _fb;  __Size[ _fb] += __Size[ _fa];
        __Depth[ _fa] = _depth;
    }
    int GetSize( int _a){
        ASSERT_WEAK_( _a>=0 && _a<__Range);
        return __Size[ GetRoot(_a)];
    }
    int64_t GetDepth( int _a){ // `a`距離*其所在並查集的根*的距離;
        ASSERT_WEAK_( _a>=0 && _a<__Range);
        GetRoot( _a); // 由於此時`Ancester[a]`可能並不是*其所在並查集的根*, 所以要把`Ancester[a]`更新為並查集的根, 這樣`Depth[a]`才是正確的;
        return __Depth[_a];
    }
};
//} ___DisjointSet_withDepth
___DisjointSet_withDepth ___DisjointSet_withDepth_?;
性质

Depth表示该点 距离其所在并查集的根的距离;

MergeTo( fa, fb, d), 注意 此时fa,fb都已经是并查集的根, 现在要将fa变成是fb儿子, 且fa距离fb的距离为d;

例題

@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=134379518;
維護兩個元素的差值, a的並查集根為fa, 說明V[a] - V[fa] = a.depth;

支持對Merge進行撤銷操作

定义

執行Merge操作後, 可以支持 把該操作撤銷, 即退回到操作之前的狀態;

性質

一個維護(集合大小size,元素深度depth)的並查集, 當你執行了mertoTo( fa, fb, d), 就相當於執行了Ancestor[fa] = fb; Size[fb] += Size[fa]; Depth[fa] = d; 此時是可以進行撤銷操作的, 也很簡單 就是針對這3個操作的逆運算;
但有一點是重要的, 即不可以再使用路徑壓縮, 即此時的GetRoot: while( __Ancester[_a] != _a){ _a = __Ancester[_a];}, 因此 時間會退化成O(N) (不再是O(1)了);
因此, 一般是針對N比較小的情況 可以使用這個算法;

代碼
class ___DisjointSet_Undo{
public:
    vector<int> __Ancester;
    int __Range;
    stack< pair<int,int> > __OperHistory; // 元素為`{fa,fb}` 說明執行了`Ancester[fa] = fb`這個操作;

    void Initialize( int _range){
        __Range = _range;
        __Ancester.resize(__Range);  ::std::iota( __Ancester.begin(), __Ancester.end(), 0);
        while( __OperHistory.empty() == false){ __OperHistory.pop();}
        // assign( __Size, 1);  memset(__Depth,0);
    }
    int GetRoot( int _a) const{
        ASSERT_WEAK_( _a>=0 && _a<__Range);
        while( __Ancester[_a] != _a){ _a = __Ancester[_a];}
        return _a;
    }
    void Merge( int _a, int _b){
        ASSERT_WEAK_( _a>=0 && _a<__Range && _b>=0 && _b<__Range);
        auto fa = GetRoot( _a), fb = GetRoot( _b);
        __OperHistory.emplace( fa, fb);
        if( fa != fb){
            __Ancester[ fa] = fb;
            // __Size[fb] += __Size[fa];  __Depth[fa] = _d(參數);
        }
    }
    void Undo(){
        ASSERT_( __OperHistory.empty() == false);
        auto fa = __OperHistory.top().first, fb = __OperHistory.top().second;  __OperHistory.pop();
        if( fa != fb){
            __Ancester[ fa] = fa;
            // __Size[fb] -= __Size[fa];  __Depth[fa] = 0;
        }
    }
    friend ostream& operator<<( ostream & _cout, const ___DisjointSet_Undo & _a){
        _cout<<"\n";
        DE_( "___DisjointSet_Undo-Debug-Begin");
        FOR_( i, 0, _a.__Range-1){
            DE_( i, _a.GetRoot(i));
        }
        DE_( "___DisjointSet_Undo-Debug-End");
    }
};
//} ___DisjointSet_Undo
例題

@LINK: https://editor.csdn.net/md/?articleId=134386573;
获取所有的生成树形态

例題

  • link

  • link (a modulo 3 3 3 group)

  • link (a modulo 2 2 2 group)

笔记

定义
A union-Set can be imagined to be a Tree, because there is only one root for a union-Set.
This is useful due to the union-set is a very abstract structure, convert a union-set to a tree is good for visualization. But it must be noted, a certain union-set is not totally has a one-to-one correspondence to a certain tree (due to Path-Compressing)

The two following tree

   f
  a d
 b
c

and

   f
a b c d

correspond to a same union-set (the latter tree is the former tree after the Path-Compressing)

Alternatively, you can always regard a union-set as the latter tree, because after the process Path-Compressing, every union-set must be the latter form tree (i.e., one root and others are its sons

There are two key properties for union-set: size and depth

#性质#

#等价关系#
The relation between two elements in Disjoint-Set must be equivalence-relation.

For instance, for an non-equivalence-relation < < <, if we already have two disjoint-sets a < b a<b a<b and c < d c<d c<d, now, given a new relation: a < c a < c a<c; according to Disjoint-Set, these 4 4 4 elements will be in the same set. However, we found that the relations of ( b , c ) ( b , d ) (b,c) (b,d) (b,c)(b,d) are not determined. So, < < < couldn’t be used in Disjoint-Set (it can be solved by Floyd-Boolean), only but for the equivalence-relation = = =.

#拓展知识点#

#集合大小#
If every node has a value v v v, then the total value of a union-set can be evaluated.

There would be a array s u m [ ] sum[] sum[], meaning the total value of the tree where the node belong to.

The total value of a union-set, only be accessed by the root of the tree. (so, if node a a a is not the root of its tree, then the answer is s u m [ R o o t ( a ) ] sum[ Root( a)] sum[Root(a)] instead of s u m [ a ] sum[ a] sum[a] ,

When m e r g e ( a , b ) merge( a, b) merge(a,b) (assuming they are at different union-set), and if we attach the tree a a a to b b b, that is, R o o t ( b ) Root( b) Root(b) will be the root of the merged tree. Then, s u m [ R o o t ( b ) ] + = s u m [ R o o t ( a ) ] sum[ Root( b)] += sum[ Root( a)] sum[Root(b)]+=sum[Root(a)]

So, the property s u m sum sum is focus on a whole tree, not a individual node.

#元素深度#
The property of d e p t h depth depth is relatively more abstract, contrast to s u m sum sum, it focus on a individual node not a whole tree.

As mentioned above, you can imagine a union-set corresponding to tree in where all other nodes be the son of the root (i.e. the form of the latter tree), and the d e p t h depth depth is the weight of a edge. (so, d e p t h depth depth is not the depth of node in a tree as usual, but there has some similar property between them, elaborating below)

More rigorously, d e p t h depth depth is not the depth of a node in its tree! it is just a n u m b e r number number, the meaning of d e p t h depth depth totally depending on how you define it.
But d e p t h depth depth must satisfying A c c u m u l a t a b i l i t y Accumulatability Accumulatability

For example,

  a
b c d

we have d e p t h [ b , c , d ] depth[ b, c, d] depth[b,c,d] be any numbers and d e p t h [ a ] depth[ a] depth[a] must equals to 0 0 0.

when this tree is merge to another tree x y z xyz xyz (in which d e p t h [ y , z ] depth[y,z] depth[y,z] be any numbers and d e p t h [ x ] depth[ x] depth[x] must equals to 0 0 0):

          x
  a			  y z
b c d

and we set d e p t h [ a ] = X depth[a] = X depth[a]=X (that is the weight of the edge a , x a,x a,x, this new edge meaning that we add this edge to merge this two union-set) the value X X X is depending on ourselves.

Now, d e p t h [ b ] depth[b] depth[b] will by d e p t h [ b ] + X depth[b] + X depth[b]+X (the same as c , d c, d c,d), this process is done automatically

So, for two union-set a , b , c , d a, b, c, d a,b,c,d and e , f , g e, f, g e,f,g, let a , e a, e a,e be two roots. If we merge this two sets, that is, connect a edge between a , e a, e a,e (the weight of this edge is X X X, it can be any number) and let e e e be the root of the merge tree.
This operation will make d e p t h [ a , b , c , d ] + = X depth[ a, b, c, d] += X depth[a,b,c,d]+=X and have no influence on d e p t h [ e , f , g ] depth[ e, f, g] depth[e,f,g]. This is the nature of the notion d e p t h depth depth
So, we costed O ( 1 ) O( 1) O(1) to finish this work, not O ( n ) O( n) O(n).

d e p t h depth depth is the most important and complicated notion in union-set.

There are some applications of d e p t h depth depth.

#阶梯型#
Regard every union-set as a echelon (i.e., the depth in a union-set must be 0 , 1 , 2 , 3... 0,1,2,3... 0,1,2,3...), e.g., one echelon A is a , b , c a, b, c a,b,c with d e p t h [ a , b , c ] = 0 , 1 , 2 depth[a,b,c] = 0,1,2 depth[a,b,c]=0,1,2 (a is root) and another echelon B is d , e , f , g d,e,f,g d,e,f,g with d e p t h [ d , e , f , g ] = 0 , 1 , 2 , 3 depth[d,e,f,g] = 0,1,2,3 depth[d,e,f,g]=0,1,2,3 (d is root)

There has a operation: append one echelon to the back of another echelon.
For example, append A to B meaning that d e p t h [ a , b , c ] = 4 , 5 , 6 depth[a,b,c] = 4,5,6 depth[a,b,c]=4,5,6 and no influence on echelon B B B

this can be solved by using union-set.

When merge A to B, that is, connect a edge between a a a and d d d (the roots of A , B A, B A,B), we set the weight of this edge (on other words, the depth of a a a in the merged tree) to the size of B B B (nodes in B B B, 4 4 4)

#取模群#
For example, there are 3 types among all objects (A, B, C), and A → B → C → A A \to B \to C \to A ABCA (where → \to means “eat”), we use [ i ] [i] [i] to denote different objects.
Given many sentences, a sentence may be contradictory (illogical) (e.g., we already had [ 1 ] → [ 2 ] [1] \to [2] [1][2] and [ 2 ] → [ 3 ] [2] \to [3] [2][3], then the sentence “ [ 1 ] [ 3 ] [1] [3] [1][3] with same type” is wrong.

In a union-set, we divide all d e p t h depth depth into 3 kinds ( 0 , 1 , 2 0, 1, 2 0,1,2) by taking % 3 \% 3 %3, and stipulating ( d 0 − d 1 ) ≡ 1 (d0 - d1) ≡ 1 (d0d1)1 meaning d 0 → d 1 d0 \to d1 d0d1.
More precisely, for any two object in this union-set [ x ] [ y ] [x] [y] [x][y], if ( d e p t h [ x ] − d e p t h [ y ] ) ≡ 1 (depth[ x] - depth[ y]) ≡ 1 (depth[x]depth[y])1, then implying that [ x ] → [ y ] [x] \to [y] [x][y].

It is very important to realize that for a object [ x ] [x] [x], d e p t h [ x ] depth[x] depth[x] is not indicating the type of [ x ] [x] [x]! we don’t know what type any object is in a union-set, union-set will not store this information, it focus on the relation.

For instance, we have a union-set with objects [ a , b , c ] [a, b, c] [a,b,c] and d e p t h [ a , b , c ] = 2 , 1 , 0 depth[ a, b, c] = 2, 1, 0 depth[a,b,c]=2,1,0. Then we will know [ a ] → [ b ] → [ c ] → [ a ] [a] \to [b] \to [c] \to [a] [a][b][c][a], but we do not know what is the type of [ a ] [a] [a].
If you want to know the type, there are many cases where this union-set may be:

  • T y p e ( a , b , c ) = A , B , C Type( a, b, c) = A, B, C Type(a,b,c)=A,B,C or
  • T y p e ( a , b , c ) = B , C , A Type( a, b, c) = B, C, A Type(a,b,c)=B,C,A or
  • T y p e ( a , b , c ) = C , A , B Type( a, b, c) = C, A, B Type(a,b,c)=C,A,B

More importantly, the type of a individual object is useless, all sentence ([a] eat [b] or [a] [b] are same type) are focusing on the relation between two object, not about a individual object.

For a sentence involving two objects [ a ] [ b ] [a] [b] [a][b], if they are not in the same set, then there is no relation between [ a ] [ b ] [a] [b] [a][b] (on other words, this sentence must be valid)

Supposing the set which contain [ a ] [a] [a] is A A A, and set B B B containing [ b ] [b] [b].

Now, due to the operation of set-merge, we need to prove that, when [ a ] [ b ] [a] [b] [a][b] is now has relationship (i.e., they will become to be in a same merged set from two disjoint set), then any object [ x ] [x] [x] in A A A and any object [ y ] [y] [y] in B B B will also have relationship (not only [ a ] [ b ] [a] [b] [a][b], because any two objects in a same set must has a specific relationship)

  • If the sentence is [a] [b] is of the same type;
    the relation between [ a ] [ x ] [a] [x] [a][x] is the same relation between [ b ] [ x ] [b] [x] [b][x] (because [ a ] [ b ] [a][b] [a][b] are of the same type)
    also, the relation between [ b ] [ y ] [b] [y] [b][y] is the same relation between [ a ] [ y ] [a] [y] [a][y]
    So, the set-merge operation is feasible for this sentence.

  • If the sentence is [a] eat [b];
    the relation between [ a ] [ x ] [a] [x] [a][x] (more specifically, that is equivalent to a number X equals X = d e p t h [ a ] − d e p t h [ x ] ( m o d   3 ) X = depth[ a] - depth[ x] (mod \ 3) X=depth[a]depth[x](mod 3), then the relation between [ b ] [ x ] [b] [x] [b][x] would equals to X − 1 ( m o d   3 ) X - 1 (mod \ 3) X1(mod 3)
    the same reasoning as [ b ] [ y ] [b][ y] [b][y].
    So, the set-merge operation is feasible for this sentence.

For example, given two disjoint set A = f a , a A = fa, a A=fa,a and B = f b , b B = fb, b B=fb,b

 fa  |  fb                                fb
a	 | b		-> after merged         fa   b        
                                       a

now there has a sentence x , y x, y x,y (x is any object in A A A, y is any object in B B B), suppose x = a , y = b x = a, y = b x=a,y=b (x, y may also be f a , f b fa, fb fa,fb, that is also feasible)

We need to calculate the weight of [ f a , f b ] [fa, fb] [fa,fb] in the new merged tree, here marked X X X. This will causes d e p t h [ f a , a ] + = X depth[ fa, a] += X depth[fa,a]+=X

  • if the sentence is a,b are of the same type
    this means that d e p t h [ a ] − d e p t h [ b ] = 0 ( m o d   3 ) depth[ a] - depth[b] = 0 (mod \ 3) depth[a]depth[b]=0(mod 3) should be equally in the new merged tree.
    so, we will get a formula (let d a da da be the d e p t h [ a ] depth[a] depth[a] in previous set, and D a Da Da be the d e p t h [ a ] depth[a] depth[a] in the merged set; d e p t h [ b ] depth[b] depth[b] is same either in previous set or the merged set); d a + X = d e p t h [ b ] ( m o d   3 ) da + X = depth[b] (mod \ 3) da+X=depth[b](mod 3), so you can calculate X X X.
  • if the sentence is a eat b
    so d e p t h [ a ] − d e p t h [ b ] = 1 ( m o d   3 ) depth[a] - depth[b] = 1 (mod \ 3) depth[a]depth[b]=1(mod 3) in the new merged tree.
    the formula d a + X − d e p t h [ b ] = 1 ( m o d   3 ) da + X - depth[ b] = 1 ( mod \ 3) da+Xdepth[b]=1(mod 3), you can get X X X.

#性质#
Although the value of d e p t h depth depth depending on you (on other words, the weight X X X of the edge used to merge two set is depending on you), but, the initial value of d e p t h depth depth is must be 0 0 0.

a  ->   b  ->   b    ->      d
      a       a   c        b   e
       (X1)    (X2)       a c
                           (X3)

X1 means the weight of the edge used to merge {a} and {b}
X2 means the weight of the edge used to merge {a,b} and {c}
X3 means the weight of the edge used to merge {a,b,c} and {d}

Then, the d e p t h depth depth in the final set equals to X 1 + X 3 X1 + X3 X1+X3 (because, X 2 X2 X2 is not the set a , b a,b a,b to be attached to another set, but is another set to be attached to a , b a, b a,b; this operation will not affect the d e p t h depth depth of a , b a,b a,b)

So, the weight X X X of the merged edge when merging two set (A merge to B, A be attached to B), only affect all depths in A A A, with no effects on B B B.

So, although the value of X X X is depending on you, the definition of d e p t h depth depth is independent.

More formally, d e p t h [ a ] depth[ a] depth[a] equals the sum of some X X X.

As a lemma, initially, there is no X X X, so, d e p t h [ a ] depth[ a] depth[a] must be 0 0 0.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值