Question: Dynamic Connectivity

Q: Given two points, are there any path connecting them? We also connect points dynamically.

Modeling

e.x: {0} {1,2,3} {4,5}: in each set points and muturally connected. If we connect 2 and 5, we can achieve a c.c: {0} {1,2,3,4,5}

Anwser 1: Quick find

Code

public class QuickFindUF
{
    private int[] id;
    // init
    public QuickFindUF(int N)
    {
        id = new int[N];
        for (int i = 0; i < N; i++)
        {
            id[i] = i;
        }
    }
    public boolean connected(int p, int q)
    {  return id[p] == id[q];  }
    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;
    }
}

Cost

Union

N: Each call of union() need at most N times of comparing and changing.

For N points, its cost’s N^2.

Find

1: Checking two value only.

Initiallize

N: Each one has to been given a value.

Summary

We can’t accept quadratic time algorithms for large problems. The reason is they don’t scale. As computers get faster and bigger, quadratic algorithms actually get slower.

This algorithm can find quickly, so we call it Quick Find. But its unioning speed is too slow.

Answer 2: Quick union

Treat these point as trees(forest):

e.x. {0} {1} {2,3,4,9} {5,6} {7} {8} ->

dynamic-connectivity-quick-union

Data Structure

Array: [id] = itsParent

Algorithm

Operation union() is faster by:

For each union(), treat the first element as child, and the other as parent.

e.x. union(9,6):

dynamic-connectivity-quick-union-2

In this case, we only need to change one value whatever the situation.

Code

public class QuickUnionUF
{
    private int[] id;
    // init
    public QuickUnionUF(int N)
    {
        id = new int[N];
        for (int i = 0; i < N; i++) id[i] = i;
    }
    private int root(int i)
    {
        while (i != 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 i = root(p);
        int j = root(q);
        id[i] = j;
    } 
}

Cost

dynamic-connectivity-quick-union-3

Connected

N: for each point, one call for root costs at most N times(include cost of root equals root comparison). So it’s 2N. ->N.

Union

N: each union() operation call for root costs at most N times(include cost of root equals root comparison). So it’s 2N. ->N.

Summary

Quick-find defect

Quick-union defect

Answer 3: Weighted Quick-union

Modify quick-union to avoid tall trees. Balance by linking root of smaller tree to root of larger tree. So we can get a lower depth of tree.

dynamic-connectivity-weighted-quick-union

How to ensure each node has a lower depth?

While unioning two trees, we just let the smaller(by numbers of size) one to below the bigger one.

e.x.

dynamic-connectivity-weighted-quick-union-2

after union(7,3)

dynamic-connectivity-weighted-quick-union-3

Why this works?

Cost

dynamic-connectivity-weighted-quick-union-4

Initialize

N: id[] + sz[] = 2N -> N.

Union(each)

lg N: T(root()) = lg N

Connectted

lg N: T(root()) = lg N

Other improvements

Path compression

dynamic-connectivity-other.png

For every node call root(), it will make this node connect directly to root.

Summary

dynamic-connectivity-other-2.png


Ref