算法 {并查集}
并查集
性质
并查集与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
;
获取所有的生成树形态
例題
笔记
定义
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
A→B→C→A (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
(d0−d1)≡1 meaning
d
0
→
d
1
d0 \to d1
d0→d1.
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) X−1(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+X−depth[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.