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();
}
}