Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weighted Quick-Union with Path Compression algorithm

There is a "Weighted Quick-Union with Path Compression" algorithm.

The code:

public class WeightedQU 
{
    private int[] id;
    private int[] iz;

    public WeightedQU(int N)
    {
        id = new int[N];
        iz = new int[N];
        for(int i = 0; i < id.length; i++)
        {
            iz[i] = i;
            id[i] = i;
        }
    }

    public int root(int i)
    {
        while(i != id[i])
        {
            id[i] = id[id[i]];   // this line represents "path compression"
            i = id[i];
        }
        return i;
    }

    public boolean connected(int p, int q)
    {
        return root(p) == root(q);
    }

    public void union(int p, int q)   // here iz[] is used to "weighting"
    {
        int i = root(p);
        int j = root(q);
        if(iz[i] < iz[j])
        {
            id[i] = j;
            iz[j] += iz[i];
        }
        else
        {
            id[j] = i;
            iz[i] += iz[j];
        }
    }
}

Questions:

  1. How does the path compression work? id[i] = id[id[i]] means that we reach only the second ancester of our node, not the root.

  2. iz[] contains integers from 0 to N-1. How does iz[] help us know the number of elements in the set?

Can someone clarify this for me?

like image 268
Aleksei Chepovoi Avatar asked Oct 02 '12 19:10

Aleksei Chepovoi


3 Answers

One more thing to be noted here:

While finding the root when we are making id[i]=id[id[i]] i.e; making i under its grand parent

-then size of id[i] will decrease by size of i i,e; iz[id[i]]-=iz[i]

Now this makes code perfectly correct.

I am not sure about this but intuitively i feel, Its absence does not cause problems because we are always comparing size of the roots.

like image 197
Gaurav jhamnani Avatar answered Sep 18 '22 23:09

Gaurav jhamnani


First understand that id is a forest. id[i] is the parent of i. If id[i] == i it means that i is a root.

For some root i (where id[i] == i) then iz[i] is the number of elements in the tree rooted at i.

public int root(int i)
{
    while(i != id[i])
    {
        id[i] = id[id[i]];   // this line represents "path compression"
        i = id[i];
    }
    return i;
}

How does the path compression work? id[i] = id[id[i]] means that we reach only the second ancester of our node, not the root.

As we are ascending the tree to find the root we move nodes from their parents to their grandparents. This partially flattens the tree. Notice that this operation doesn't change which tree the node is a member of, this is all we are interested in. This is the path compression technique.

(You did notice the loop right? while(i == id[i]) terminates once i is a root node)

iz[] contains integers from 0 to N-1. How does iz[] help us know the number of elements in the set?

There is a transcription error in the code:

for(int i = 0; i < id.length; i++)
{
    iz[i] = i; // WRONG
    id[i] = i;
}

This is the correct version:

for(int i = 0; i < id.length; i++)
{
    iz[i] = 1; // RIGHT
    id[i] = i;
}

iz[i] is the number of elements for a tree rooted at i (or if i is not a root then iz[i] is undefined). So it should be initialized to 1, not i. Initially each element is a seperate "singleton" tree of size 1.

like image 42
Andrew Tomazos Avatar answered Sep 20 '22 23:09

Andrew Tomazos


id[i] = id[id[i]]; // this line represents "path compression"

The above code is "Simpler one-pass variant" as mentioned in the slide of Union Find (Algorithms, Part I by Kevin Wayne and Robert Sedgewick). Therefore your guess for question 1 is correct. Each examined node points to its grandparent.

To make each examined node points to the root we will need two-pass implementation:

  /**
 * Returns the component identifier for the component containing site <tt>p</tt>.
 * @param p the integer representing one site
 * @return the component identifier for the component containing site <tt>p</tt>
 * @throws java.lang.IndexOutOfBoundsException unless 0 <= p < N
 */
public int find(int p) {
    int root = p;
    while (root != id[root])
        root = id[root];
    while (p != root) {
        int newp = id[p];
        id[p] = root;
        p = newp;
    }
    return root;
}

Reference: http://algs4.cs.princeton.edu/15uf/WeightedQuickUnionPathCompressionUF.java.html

like image 28
DML Avatar answered Sep 19 '22 23:09

DML