Dynamic connectivity:
Union Command: connect two objects
Find query: is there a path connecting the two objects?
Quick Find:
用一个数组来表示所有connected component。每个数字顶点在数组中都有一个set index。如果两个顶点的set index是一样的,则表示它们是连接的。
容易find,但union复杂。union(a,b) 将a所在的connected component的set index改成b的set index。
public class QuickFind {
private int[] id;
public QuickFind(int N) {
id = new int[N];
// Initialization
for (int i = 0; i < N; i++) {
id[i] = i;
}
}
// O(1) access
public boolean connected(int p, int q) {
return id[p] == id[q];
}
// O(N) access
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;
}
}
}
}
Quick Union:
树形结构,每个数字顶点都有一个set index,指向它的parent顶点。每个数字顶点都有一个root,如果两个顶点的root一样,则表示它们是连接的。
Weighted Quick Union:
总是把小的树作为子树。Always put smaller tree below. 树最深为lgN
Path Compression:
所经路径顶点都指向root。
WQUPC:
public class QuickUnion {
private int[] id;
private int[] size;
public QuickUnion(int N) {
id = new int[N];
size = new int[N];
for (int i = 0; i < N; i++) {
id[i] = i;
size[i] = 1;
}
}
private int root(int i) {
while (i != id[i]) {
id[i] = id[id[i]]; // Path compression
i = id[i];
}
return i;
// return i == id[i] ? i : root(id[i]); no path compression
}
public boolean connected(int p, int q) {
return root(p) == root(q);
}
public void union(int p, int q) {
int proot = root(p);
int qroot = root(q);
if (proot == qroot) {
return;
}
// Smaller tree as subtree of larger tree
if (size[proot] < size[qroot]) {
id[proot] = qroot;
size[qroot] += size[proot];
} else {
id[qroot] = proot;
size[proot] += size[qroot];
}
}
}
应用:
Percolation:
黑白块,如果从上到下有白块通路,则system percolates。
0 |
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 |
0 |
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 |
public class Percolation {
private int[] id;
private int[] size;
private boolean[] color;
private int width;
private int top;
private int bot;
public Percolation(int N) {
width = N;
top = N * N;
bot = top + 1;
id = new int[N * N + 2];
size = new int[N * N + 2];
color = new boolean[N * N + 2];
for (int i = 0; i < N * N + 2; i++) {
id[i] = i;
size[i] = 1;
}
color[top] = true;
color[bot] = true;
}
private int root(int i) {
while (i != id[i]) {
id[i] = id[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 proot = root(p);
int qroot = root(q);
if (proot == qroot) {
return;
}
if (size[proot] <= size[qroot]) {
id[proot] = qroot;
size[qroot] += size[proot];
} else {
id[qroot] = proot;
size[proot] += size[qroot];
}
}
public void openSite(int p) {
color[p] = true;
if (p < width) {
union(p, top);
}
if (p >= (width - 1) * width) {
union(p, bot);
}
int row = p / width;
int col = p % width;
if (row - 1 >= 0 && color[p - width]) {
union(p, p - width);
}
if (row + 1 < width && color[p + width]) {
union(p, p + width);
}
if (col - 1 >= 0 && color[p - 1]) {
union(p, p - 1);
}
if (col + 1 < width && color[p + 1]) {
union(p, p + 1);
}
}
public boolean isPercolate() {
return root(top) == root(bot);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < width * width; i++) {
if (color[i]) {
sb.append("O");
} else {
sb.append("X");
}
if ((i + 1) % width == 0) {
sb.append('\n');
}
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}