• 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;
        }
    }
  • 相关阅读:
    全排列生成算法
    Jekyll + Github 搭建属于你的静态博客
    Merge k Sorted Lists
    Manacher's algorithm
    ADWORLD web/warmup
    数据结构/chap1 &chap2/选择判断/复习
    数据结构/PTA-两个有序链表序列的交集/链表
    数据结构/PTA-符号配对/栈
    数据结构/PTA-列车调度/栈/数组
    数据结构/PTA-堆栈操作合法性/数组/
  • 原文地址:https://www.cnblogs.com/zmyvszk/p/5351494.html
Copyright © 2020-2023  润新知