Leetcode总结之Union Find

package UnionFind;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class UnionFindProblem {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }
    /*
     * 网上查了一下:http://blog.csdn.net/dm_vincent/article/details/7655764
     * 这个人写的不错,但是他是抄袭这个普林斯顿的教授的视频:
     * https://www.youtube.com/watch?v=H0bkmI1Xsxg&list=PLe-ggMe31CTexoNYnMhbHaWhQ0dvcy43t&index=2#t=379.668
     * Union Find主要有两个部分,一个部分是Find Query,Check if two objects are in the same component.
     * 另一个部分是Union Command,也就是replace components containing two objects with their union.
     * 那么我们构建一个UF class
     * public class UF
     * UF(int N)------initialize with N object
     * void union(int p,int q)--------add connection between p and q
     * boolean connected(int p,int q)-------are p and q in the same component?
     * 那么如何来构建Data Structure呢?
     * Integer array id[] of size N and p and q are connected iff they have the same id.
     *       0 1  2 3  4  5 6 7 8
     * id[]  0 1  1 8  8  0 0 1 8 8表明0,5,6相连的
     * 那么Find就只需要看id相不相等,然后Union就相对来说比较复杂
     * To Merge components containing p and q, change all entries whose id equals id[p] to id[q].
     * id[]  1 1 1 8 8 1 1 1 8 8       after union of 6 and 1
     */
    public class QuickFindUF{
        private int[] id;
        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;
        }
        /*
         * 那么我们能不能再把程序更进步一下吗?Quick-Union还是一样的ds,但是,这里id[i] is parent of i,
         * Root of i is id[id[id[..id[i]..]]].Keep going until it doesn‘t change
         *     0 1 2 3 4 5 6 7 8 9
         * id[]0 1 9 4 9 6 6 7 8 9
         * 0   1    9     6   7   8
         *         / \    |
         *         2 4    5
         *           |
         *           3
         * 这里9,2,4,3在一起
         * check if p and q have the same root
         * Union to merge components containing p and q, set the id of p‘s root to the id of q‘s root
         */
        public class QuickUnionUF2{
            private int[] id;
            public QuickUnionUF2(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;
            }
        }
    }
    /*
     * 上面两种的比较,
     *           initialize    union    find    defect
     * quick-find    N          N        1      Union too expensive
     * quick-union   N          N        N      Find too expensive,tree are too tall
     * weightd QU    N         lgN      lgN
     * 我们会发现如果用quick union的话树会非常的长,所以我们不能固定模式的union(a,b)一定是把b加到a的子树,我们
     * 应该看ab树的大小,把小的放在大的下面,这样可以节省一部分查找时间
     */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /*
     * 130. Surrounded Regions
     * 2016-4-3 by Mingyang
     * union 什么:所有从边界可达的O元素union在一起
     * union 目的:union完成后那些没有在边界可达O集合中的O是需要翻转的
     */
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0].length == 0)
            return;
        int rows = board.length, cols = board[0].length;
        int oRoot = rows * cols;
        initUnionFind(rows * cols);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == ‘X‘) continue;
                int curr = i * cols + j;
                if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) {
                    union(curr, oRoot);
                } else {
                    if (j + 1 < cols && board[i][j + 1] == ‘O‘)
                        union(curr, i * cols + j + 1);
                    if (j - 1 >= 0 && board[i][j - 1] == ‘O‘)
                        union(curr, i * cols + j - 1);
                    if (i + 1 < rows && board[i + 1][j] == ‘O‘)
                        union(curr, (i + 1) * cols + j);
                    if (i - 1 >= 0 && board[i - 1][j] == ‘O‘)
                        union(curr, (i - 1) * cols + j);
                }
            }
        }
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == ‘O‘ && find(i * cols + j) != oRoot) {
                    board[i][j] = ‘X‘;
                }
            }
        }
    }
    int[] s;
    int[] rank;
    private void initUnionFind(int n) {
        s = new int[n + 1];
        rank = new int[n + 1];
        for (int i = 0; i <= n; i++)
            s[i] = i;
        rank[n] = n + 1;
    }
    private int find(int p) {
        if (s[p] == p) return p;
        else return s[p] = find(s[p]);
    }
    private void union(int p, int q) {
        int pRoot = find(p), qRoot = find(q);
        if (pRoot == qRoot) return;
        if (rank[pRoot] < rank[qRoot]) {//保证小的树在大的下面
            s[pRoot] = qRoot;
        } else {
            if (rank[pRoot] == rank[qRoot])
                rank[pRoot]++;
            s[qRoot] = pRoot;
        }
    }
    /*
        * 200.Number of Islands
        * 2016-4-3 by Mingyang
        * union 什么:两个相邻的1元素
     * union 目的:union后计数union集合数量(通过计数union数组中根节点数量)
        */
    class UF {
        public int count = 0;
        public int[] id = null;
        public UF(int m, int n, char[][] grid) {
            for(int i = 0; i < m; i++) {
                for(int j = 0; j < n; j++) {
                    if(grid[i][j] == ‘1‘) count++;
                }
            }
            id = new int[m * n];
            for(int i = 0; i < m * n; i++) {
                id[i] = i;
            }
        }
        public int find(int p) {
            while(p != id[p]) {
                id[p] = id[id[p]];
                p = id[p];
            }
            return p;
        }
        public boolean isConnected(int p, int q) {
            int pRoot = find(p);
            int qRoot = find(q);
            if(pRoot != qRoot) return false;
            else return true;
        }
        public void union(int p, int q) {
            int pRoot = find(p);
            int qRoot = find(q);
            if(pRoot == qRoot) return;
            id[pRoot] = qRoot;
            count--;
        }
        }
    public int numIslands(char[][] grid) {
            if(grid.length == 0 || grid[0].length == 0) return 0;
            int m = grid.length, n = grid[0].length;
            UF uf = new UF(m , n, grid);
            for(int i = 0; i < m; i++) {
                for(int j = 0; j < n; j++) {
                    if(grid[i][j] == ‘0‘) continue;
                    int p = i * n + j;
                    int q;
                    if(i > 0 && grid[i - 1][j] == ‘1‘) {
                        q = p - n;
                        uf.union(p, q);
                    }
                    if(i < m - 1 && grid[i + 1][j] == ‘1‘) {
                        q = p + n;
                        uf.union(p, q);
                    }
                    if(j > 0 && grid[i][j - 1] == ‘1‘) {
                        q = p - 1;
                        uf.union(p, q);
                    }
                    if(j < n - 1 && grid[i][j + 1] == ‘1‘) {
                        q = p + 1;
                        uf.union(p, q);
                    }
                }
            }
            return uf.count;
        }
    //当然你也会觉得下面的可能更简单,那就是另外一种情况了,
    //设一个叫count的值,没遇到一个1,就把所有相连的1全部变为0,这样,到底遇到几次1,就是最终有几个小岛啦
       public int numIslands2(char[][] grid) {
           if (grid == null || grid.length == 0 || grid[0].length == 0)
               return 0;
           int count = 0;

           for (int i = 0; i < grid.length; i++) {
               for (int j = 0; j < grid[0].length; j++) {
                   if (grid[i][j] == ‘1‘) {
                       count++;
                       dfs(grid, i, j);
                   }
               }
           }
           return count;
       }
       public void dfs(char[][] grid, int i, int j) {
           // validity checking
           if (i < 0 || j < 0 || i > grid.length - 1 || j > grid[0].length - 1)
               return;

           // if current cell is water or visited
           if (grid[i][j] != ‘1‘)
               return;

           // set visited cell to ‘0‘
           grid[i][j] = ‘0‘;

           // merge all adjacent land
           dfs(grid, i - 1, j);
           dfs(grid, i + 1, j);
           dfs(grid, i, j - 1);
           dfs(grid, i, j + 1);
       }
    /*
     * 261.Graph Valid Tree
     * 2016-4-3 by Mingyang
     * 我们在Graph里面用其他方法做了一下这里我们再用并查集来做
     * union 什么:一条边的两个顶点
     * union 目的:若union两个顶点时发现根一样,说明已经在同一棵树中,
     * 说明输入graph存在环,非tree;union结束后,计数有多少个不同的根,当且仅当存在一个根时事vaild tree
     */
       public boolean validTree(int n, int[][] edges) {
        UnionFind uf = new UnionFind(n);
        for(int i = 0; i < edges.length; i++){
            // 如果两个节点已经在同一集合中,说明新的边将产生环路
            if(!uf.union(edges[i][0], edges[i][1])){
                return false;
            }
        }
        return uf.count() == 1;
    }
    public class UnionFind {
        int[] ids;
        int cnt;
        public UnionFind(int size){
            this.ids = new int[size];
            //初始化并查集,每个节点对应自己的集合号
            for(int i = 0; i < this.ids.length; i++){
                this.ids[i] = i;
            }
            this.cnt = size;
        }
        public boolean union(int m, int n){
            int src = find(m);
            int dst = find(n);
            //如果两个节点不在同一集合中,将两个集合合并为一个
            if(src != dst){
                for(int i = 0; i < ids.length; i++){
                    if(ids[i] == src){
                        ids[i] = dst;
                    }
                }
                // 合并完集合后,集合数减一
                cnt--;
                return true;
            } else {
                return false;
            }
        }
        public int find(int m){
            return ids[m];
        }
        public int count(){
            return cnt;
        }
    }
       /*
     * 305    Number of Islands II
     * 2016-4-3 by Mingyang
     * Given a n,m which means the row and column of the 2D matrix and an array of pair A( size k ).
     * Originally, the 2D matrix is all 0 which means there is only sea in the matrix.
     * The list pair has k operator and each operator has two integer A[i].x, A[i].y means
     * that you can change the grid matrix[A[i].x][A[i].y] from sea to island.
     * Return how many island are there in the matrix after each operator.
     */
     private int[][] dir = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
     public List<Integer> numIslands2(int m, int n, int[][] positions) {
            UnionFind2D islands = new UnionFind2D(m, n);
            List<Integer> ans = new ArrayList<Integer>();
            for (int[] position : positions) {
                int x = position[0], y = position[1];
                int p = islands.add(x, y);
                for (int[] d : dir) {
                    int q = islands.getID(x + d[0], y + d[1]);
                    if (q > 0 && !islands.find(p, q))
                        islands.unite(p, q);
                }
                ans.add(islands.size());
            }
            return ans;
        }
}
class UnionFind2D {
    private int[] id;
    private int[] sz;
    private int m, n, count;
    public UnionFind2D(int m, int n) {
        this.count = 0;
        this.n = n;
        this.m = m;
        this.id = new int[m * n + 1];
        this.sz = new int[m * n + 1];
    }
    public int index(int x, int y) { return x * n + y + 1; }
    public int size() { return this.count; }
    public int getID(int x, int y) {
        if (0 <= x && x < m && 0<= y && y < n)
            return id[index(x, y)];
        return 0;
    }
    public int add(int x, int y) {
        int i = index(x, y);
        id[i] = i; sz[i] = 1;
        ++count;
        return i;
    }
    public boolean find(int p, int q) {
        return root(p) == root(q);
    }
    public void unite(int p, int q) {
        int i = root(p), j = root(q);
        if (sz[i] < sz[j]) { //weighted quick union
            id[i] = j; sz[j] += sz[i];
        } else {
            id[j] = i; sz[i] += sz[j];
        }
        --count;
    }
    private int root(int i) {
        for (;i != id[i]; i = id[i])
            id[i] = id[id[i]]; //path compression
        return i;
    }
}
时间: 2024-12-28 20:46:53

Leetcode总结之Union Find的相关文章

[LeetCode] Find the k-th Smallest Element in the Union of Two Sorted Arrays

Question: Given two sorted arrays A, B of size m and n respectively. Find the k-th smallest element in the union of A and B. You can assume that there are no duplicate elements. http://leetcode.com/2011/01/find-k-th-smallest-element-in-union-of.html

Leetcode 128. Longest Consecutive Sequence (union find)

Given an unsorted array of integers, find the length of the longest consecutive elements sequence. Your algorithm should run in O(n) complexity. Example: Input: [100, 4, 200, 1, 3, 2] Output: 4 Explanation: The longest consecutive elements sequence i

leetcode 261-Graph Valid Tree(medium)(BFS, DFS, Union find)

Given n nodes labeled from 0 to n-1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree. 这道题主要就是判断1.是否有环路 2. 是否是连通图 可以用DFS, BFS 和 Union find,union find最合适. 对于DFS和BFS,首先都是建立

[LeetCode] 547. Friend Circles

https://leetcode.com/problems/friend-circles public class Solution { int[] id; int[] weight; public int findCircleNum(int[][] M) { if (M == null || M.length == 0 || M[0].length == 0) { return 0; } initUnionFind(M.length); Set<Integer> set = new Hash

LeetCode 第二题,Median of Two Sorted Arrays

题目再现 There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). 题意解析 题目意思是给两个大小为m,n的有序数组(m,n可能为0),要求找出这两个数组的中位数.并且程序的时间复杂度必须不能超过O(log(m+n)). 这道题的

[LeetCode] Second Highest Salary 第二高薪水

Write a SQL query to get the second highest salary from the Employee table. +----+--------+ | Id | Salary | +----+--------+ | 1 | 100 | | 2 | 200 | | 3 | 300 | +----+--------+ For example, given the above Employee table, the second highest salary is 

LeetCode Graph Valid Tree

原题链接在这里:https://leetcode.com/problems/graph-valid-tree/ 题目: Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree. For example: Given n

LeetCode Friend Circles

原题链接在这里:https://leetcode.com/problems/friend-circles/description/ 题目: There are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direc

[LeetCode] 200. Number of Islands 岛屿的数量

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by