-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathUnionFind.cpp
More file actions
145 lines (117 loc) · 3.54 KB
/
UnionFind.cpp
File metadata and controls
145 lines (117 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <vector>
#include <map>
#include <algorithm>
// Spaghetti Source のUnionFind(まんま)
// http://www.prefield.com/algorithm/container/union_find.html
// UnionFind木
// unite()で2つの集合を1つに統合する
// find()で2つの集合が同じ集合かどうかわかる
// root()で集合の識別番号(その集合のルートの番号)を求められる
struct UnionFind{
std::vector<int> data;
// dataの各要素について
// 負の値:その集合のルートであること示す。また、その絶対値は集合の要素数となっている。
// 正の値:親ノードの番号(dataのインデックス)。root()を呼び出すたびに集合のルートを指すように書きなおされるので木はそんなに深くならない
//初期化 size:最大要素数
UnionFind(int size): data(size, -1) {}
// 集合を併合する
// すでに同じ集合だった場合は、falseが返る
bool unite(int x, int y){
x=root(x);
y=root(y);
if( x != y ){
// 要素数の大きな方へ合併するためのswap
if( data[y] < data[x] ) std::swap(x, y);
// 要素数を加算する
data[x] += data[y];
// yの属する集合のルートをxに変更
data[y] = x;
}
return x!=y;
}
// 同じ集合かどうか判定
bool find(int x, int y){
return root(x) == root(y);
}
// 集合の識別番号を返す
int root(int x){
// 負の値を持つものがその集合のルート
// 正の値は同じ集合に属するものを指す(辿ればいずれルートへ着く)
return (data[x] < 0)? x : data[x]=root(data[x]);
}
// 集合の要素数を返す
int size(int x){
return -data[ root(x) ];
}
};
// 連番でない数字やint以外を扱う
// 次の一文を有効にすると2倍速になるがmake()を呼ぶのが必須となる
// #define get_index(a) data[(a)]
template < typename T >
struct UnionFindMap{
std::map<T, int> data;
UnionFind uf;
// 初期化 size:最大要素数
UnionFindMap(int size): uf(size){ }
#ifndef get_index
// 要素識別番号
int get_index(T x){
// map内に無かったら追加
if( data.find(x) == data.end() ){
data.insert( std::make_pair<T, int>( x, data.size() ) );
}
return data[x];
}
#endif
// 集合を作る
// べつにこの関数は呼ばなくても良い
int make(T x){
// map内に無かったら追加
if( data.find(x) == data.end() ){
data.insert( std::make_pair<T, int>( x, data.size() ) );
}
return data[x];
}
// 集合を併合する
bool unite(T x, T y){
return uf.unite( get_index(x), get_index(y) );
}
// 同じ集合かどうか判定
bool find(T x, T y){
return uf.find( get_index(x), get_index(y) );
}
// 集合識別番号を返す
int root(T x){
return uf.root( get_index(x) );
}
};
//テスト
#include <cstdio>
using namespace std;
int maxuf=10;
int main(){
char n, m;
char a;
UnionFindMap<char> uf(maxuf);
printf("%d個の文字を入力\n", maxuf);
for(int i=0; i<10; i++){
scanf(" %c", &a);
uf.make(a);
}
printf("合わせる2つの集合を指定\n");
while( scanf(" %c %c", &n, &m), n||m ){
printf("%c , %c \n", n, m);
printf("find: %d\n", uf.find(n, m) );
uf.unite(n, m);
for(map<char,int>::iterator it=uf.data.begin(); it!=uf.data.end(); it++)
printf("%2c ", (*it).first);
printf("\n");
for(map<char,int>::iterator it=uf.data.begin(); it!=uf.data.end(); it++)
printf("%2d ", (*it).second);
printf("\n");
for(map<char,int>::iterator it=uf.data.begin(); it!=uf.data.end(); it++)
printf("%2d ", uf.uf.data[(*it).second] );
printf("\n");
}
return 0;
}