1. Union-Find Flashcards
We assume in Dynamic connectivity that “is connected to” is an equivalence relation:
transitive:
If p is connected to q and q is connected to r, then p is connected to r
We assume in Dynamic connectivity that “is connected to” is an equivalence relation:
symmetric:
If p is connected to q, then q is connected to p.
We assume in Dynamic connectivity that “is connected to” is an equivalence relation:
reflexive:
p is connected to p.
An equivalence relation partitions the objects into
equivalence classes or connected components.
Quick-find
maintains the invariant that p and q are connected if and only if id[p] is equal to id[q]. In other words, all sites in a component must have the same value in id[].
//QuickFindUF.java
public class QuickFindUF {
private int[] id; // id[i] = component identifier of i
private int count; // number of components
public QuickFindUF(int n) {
count = n;
id = new int[n];
for (int i = 0; i < n; i++)
id[i] = i;
}
//QuickFindUF.java
/**
* Returns the number of sets
*/
public int count() {
return count;
}
//QuickFindUF.java
/**
* Returns the canonical element of the set containing element {@code p}.
*/
public int find(int p) {
validate(p);
return id[p];
}
//QuickFindUF.java
// validate that p is a valid index
private void validate(int p) {
int n = id.length;
if (p < 0 || p >= n) {
throw new IllegalArgumentException(“index “ + p + “ is not between 0 and “ + (n-1));
}
}
//QuickFindUF.java
/**
* Returns true if the two elements are in the same set.
*/
public boolean connected(int p, int q) {
validate(p);
validate(q);
return id[p] == id[q];
}
//QuickFindUF.java
/**
* Merges the set containing element
*/
public void union(int p, int q) {
validate(p);
validate(q);
int pID = id[p]; // needed for correctness
int qID = id[q]; // to reduce the number of array accesses
// p and q are already in the same component if (pID == qID) return; for (int i = 0; i < id.length; i++) if (id[i] == pID) id[i] = qID; count--; }
//QuickFindUF.java
/**
* Reads an integer {@code n} and a sequence of pairs of integers
* (between {@code 0} and {@code n-1}) from standard input, where each integer
* in the pair represents some element;
* if the elements are in different sets, merge the two sets
* and print the pair to standard output.
*/
public static void main(String[] args) {
int n = StdIn.readInt();
QuickFindUF uf = new QuickFindUF(n);
while (!StdIn.isEmpty()) {
int p = StdIn.readInt();
int q = StdIn.readInt();
if (uf.find(p) == uf.find(q)) continue;
uf.union(p, q);
StdOut.println(p + “ “ + q);
}
StdOut.println(uf.count() + “ components”);
}
}
Quick-union
is based on the same data structure—the site-indexed id[] array—but it uses a different interpretation of the values that leads to more complicated structures. Specifically, the id[] entry for each site will be the name of another site in the same component (possibly itself). To implement find() we start at the given site, follow its link to another site, follow that sites link to yet another site, and so forth, following links until reaching a root, a site that has a link to itself. Two sites are in the same component if and only if this process leads them to the same root. To validate this process, we need union() to maintain this invariant, which is easily arranged: we follow links to find the roots associated with each of the given sites, then rename one of the components by linking one of these roots to the other.
//QuickUnionUF.java
public class QuickUnionUF {
private int[] parent; // parent[i] = parent of i
private int count; // number of components
/** * Initializes an empty union-find data structure with */ public QuickUnionUF(int n) { parent = new int[n]; count = n; for (int i = 0; i < n; i++) { parent[i] = i; } }
//QuickUnionUF.java
/**
* Returns the number of sets.
*/
public int count() {
return count;
}