132.1.001 Union-Find | 并查集

Posted neo007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了132.1.001 Union-Find | 并查集相关的知识,希望对你有一定的参考价值。

@(132 - ACM | 算法)

Algorithm | Coursera - by Robert Sedgewick

> Tip: Focus on WHAT is really important! 
> Don't just copy it!
> Don't look at the subtitle
> Practice is the key. Just Do it!

Backup

P.S. iff == if and only if

0 Introduction

  • Dynamic connectivity problem
  • union-find data type
  • quick find
  • quick union
  • improvements
    • weighted quick union
    • weighted quick union with path compression
  • applications
    • Maze Problem

1 Steps to developing a usable algorithm

  • Model the problem
  • Find a algorithm
  • Fast enough? Fits in memory?
  • if not, figure out why
  • Find a way to address the problem
  • Iterate until satisfied

2 Quick Find | 快速查找

Structure - Linear
技术分享图片

Java implementation

public class QuickFindUF
{
    private int[] id;
    
    //constructor
    public QuickFindUF(int N)
    {
        id = new int[N];//allocate N*int
        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;
        }
    }
}

Quick find is too slow

技术分享图片

3 Quick Union

Structure-Tree

  • Check if they have the same root
    inspire: use the third standard as Reference//第三方标准作为参照物,语言同理
    技术分享图片

Java implementation

public class QuickUnionUF
{
    private int[] id;
    //constructor —— set each element to be its own root
    public QuickUnionUF(int N)
    {
        id = new int[N];
        for (int i = 0;i < N;i++) id[i] = i;
    }
    //find the root by chasing parent pointers
    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;
    }
}

Quick Union is also too slow

技术分享图片

4 Quik-Union Improvement1 -Weighted quick-union

smaller tree down below - small depends on the bigger( size)
技术分享图片

demo
技术分享图片

improvement

技术分享图片

Java implementation

//Data Structure 
//maintain extra array sz[i] to count number of objects in the tree rooted at i
//sz[i] = size of tree rooted i


// Find - identical to quick-union

//Union
//Modify quick-union to:
//1.Link root of smaller tree to root of larger tree.
//2.Update the sz[] array
    int i = root(p);
    int j = root(q);
    if (i == j) return;
    if (sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]};
    else               { id[j] = i; sz[i] += sz[j]};

Runing time

O(N) = lg N


技术分享图片

5 Quik-Union Improvement2 -Path compression

Flatten the tree
In practice - Keeps tree almost completely flat.

技术分享图片


技术分享图片

java implementation

  • Make every other node in path point to its grandparent

private int root(int i)
{
    while(i != id[i])
    {
        id[i] = id[id[i]];//only one extra line of code!
        i = id[i];
    }
    return i;
}

O(N) = N+M * lgN

6 Summary for solving the dynamic connectivity problem

技术分享图片

7 Union-Find Application

技术分享图片

Percolation

技术分享图片

Monte Carlo simulation //蒙特卡罗模拟

技术分享图片

Dynamic connectivity solution to estimate percolation threshold

  • Clever Trick
    技术分享图片

8 Application - Percolation | 渗滤问题

需要注意的是Timing和Backwash的问题

  • Timing:PercolationStats.java里StdRandom.mean()和StdRandom.stddev()都只能调用一次;Percolation.java里实现numberOfOpenSites时切记不能使用循环累加,定义一个私有属性来计数即可;实现open()时相邻的四个sites位置直接加减n或1即可。

  • Backwash:实现isFull()时需要另外实例化一个不包含最下端虚拟节点的WeightedQuickUnionUF,可以解决Test 13: check for backwash with predetermined sites,Test 14: check for backwash with predetermined sites that have multiple percolating paths和Test 15: call all methods in random order until all sites are open, allowing isOpen() to be called on a site more than once

三项测试无法通过的问题。Backwash问题是指因为虚拟底部结点的存在,导致底部任一结点渗漏成功的话底部所有结点都会认为渗漏成功。原因是通过底部虚拟结点形成了回流。从而导致isFull()方法出错。

参考链接

  • Percolation.java
import edu.princeton.cs.algs4.WeightedQuickUnionUF;
 
public class Percolation {
    
    private boolean[] op; // true=open while false=blocked
    private int side; // number of rows or columns
    private int numOp; // number of open sites
    private WeightedQuickUnionUF uf;
    private WeightedQuickUnionUF ufTop;
 
    public Percolation(int n) {
        
        if(n <= 0) throw new IllegalArgumentException("Input should be positif!
");
        
        this.side = n;
        this.op = new boolean[n*n+2]; // with 2 virtual sites
        this.uf = new WeightedQuickUnionUF(n*n+2); 
        this.ufTop = new WeightedQuickUnionUF(n*n+1); // with only the upper virtual site
        
        for(int i=1; i<n*n+1; i++) op[i] = false;
        op[0] = op[n*n+1] = true;
        this.numOp = 0;
        
    }
    
    // both ROW and COL should be integer within 1~n
    private void checkBounds(int row, int col){
        if(row < 1 || row > this.side || col < 1 || col > this.side){
            throw new IllegalArgumentException("Index out of bounds!
");
        }
    }
    
    // get position of sites in 3 arrays: op, uf.parent & uf.size
    private int getPosition(int row, int col){
        return (row - 1) * this.side + col; 
    }
    
    private void union(int aPos, int bPos, WeightedQuickUnionUF wq){
        if(!wq.connected(aPos, bPos)){
            wq.union(aPos, bPos);
        }
    }
    
    private boolean isOpen(int pos){
        return op[pos];
    }
    
    public void open(int row, int col) {
        
        checkBounds(row, col);  
        if(isOpen(row, col)) return;
        
        int pos = getPosition(row, col);
        op[pos] = true;
        numOp++;
        
        // positions of adjacent sites
        int rowPrev = pos - side, rowNext = pos + side,
                colPrev = pos - 1, colNext = pos + 1;
        
        // try connect the adjacent open sites
        if(row == 1){
            union(0, pos, uf);
            union(0, pos, ufTop);
        }else if(isOpen(rowPrev)){
            union(rowPrev, pos, uf);
            union(rowPrev, pos, ufTop);
        }
                
        if(row == side){
            union(side * side + 1, pos, uf);
        }else if(isOpen(rowNext)){
            union(rowNext, pos, uf);
            union(rowNext, pos, ufTop);
        }
        
        if(col != 1 && isOpen(colPrev)) {
            union(colPrev, pos, uf);
            union(colPrev, pos, ufTop);
        }
        
        if(col != side && isOpen(colNext)) {
            union(colNext, pos, uf);
            union(colNext, pos, ufTop);
        }
    }
    
    public boolean isOpen(int row, int col) {
        checkBounds(row, col);
        return isOpen(getPosition(row, col));
                    
    }
    
    /**
     * check for backwash with predetermined sites that have multiple percolating paths
     * in this case ufTop should be used instead of uf
     * @param row
     * @param col
     * @return
     */
    public boolean isFull(int row, int col) {
        checkBounds(row, col);
        //return uf.connected(0, getPosition(row, col)); -> didn't pass the test! 
        return ufTop.connected(0, getPosition(row, col));
            
    }
    
    // should pass the timing check
    public int numberOfOpenSites(){
        return this.numOp;
    }
    
    public boolean percolates(){
        return uf.connected(0, side * side + 1);
    }
 
}

  • PercolationStats.java

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.StdStats;
import edu.princeton.cs.algs4.Stopwatch;
 
public class PercolationStats {
    
    private double[] results; // estimated threshold for each trial
    private double avg;
    private double std;
    
    public PercolationStats(int n, int trials){
        
        if(n <= 0 || trials <= 0) throw new IllegalArgumentException();
        
        results = new double[trials];
        for(int i = 0; i < trials; i++){
            int step = 0;
            Percolation pr = new Percolation(n);
            while(!pr.percolates()){
                int row = StdRandom.uniform(n) + 1;
                int col = StdRandom.uniform(n) + 1;
                if(!pr.isOpen(row, col)){
                    pr.open(row, col);
                    step++;
                }
            }
            results[i] = (double)step / (n * n);
        }
        
        this.avg = StdStats.mean(results);
        this.std = StdStats.stddev(results);
        
    }
    
    public static void main(String[] args){
        
        StdOut.printf("%-25s
", "Please input 2 integers");
        int N = StdIn.readInt();
        int T = StdIn.readInt();
        
        Stopwatch wt = new Stopwatch();
        
        PercolationStats ps = new PercolationStats(N, T);
        
        // elapsed CPU time in seconds
        double elapsed = wt.elapsedTime();
        
        StdOut.printf("%-25s= %.15f
", "elapsed CPU time", elapsed);
        StdOut.printf("%-25s= %.7f
", "mean", ps.mean());
        StdOut.printf("%-25s= %.17f
", "stddev", ps.stddev());
        StdOut.printf("%-25s= [%.15f, %.15f]
", "%95 confidence interval", 
                ps.confidenceLo(), ps.confidenceHi());
    }
    
    public double mean(){
        return this.avg;
    }
    
    public double stddev(){
        return this.std;
    }
    
    public double confidenceLo(){
        return mean() - 1.96 * stddev() / Math.sqrt(results.length);
    }
    
    public double confidenceHi(){
        return mean() + 1.96 * stddev() / Math.sqrt(results.length);
    }
 
}

以上是关于132.1.001 Union-Find | 并查集的主要内容,如果未能解决你的问题,请参考以下文章

Union-Find(并查集): Quick union improvements

Union-Find 并查集算法

算法(第四版)之并查集(union-find算法)

Almost Union-Find 并查集的删除

UVA - 11987 Almost Union-Find[并查集 删除]

LeetCode并查集 union-find(共16题)