zoukankan      html  css  js  c++  java
  • 并查集

    1. 概念

    并查集(Union Find)是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

    功能:

    a. 查找两个元素是否属于同一个集合:isSameSet(A,B) A所在的集合为Set1,B所在的集合为Set2,则返回Set1和Set2是否属于同一个集合;

    b. 将两个元素各自所在的集合合并到一起

    
    
    class UnionFind {
        public static class Node{
            //whataver you like
            int x, y;
            public Node(int x, int y){
                this.x = x;
                this.y = y;
            }
        }
        public static class unionFindSet{
            public HashMap<Node, Node> fatherMap;
            public HashMap<Node, Integer> sizeMap;
            public unionFindSet(List<Node> nodes){
                makeSets(nodes);
            }
            private void makeSets(List<Node> nodes){
                fatherMap = new HashMap<>();
                sizeMap = new HashMap<>();
                for(Node node : nodes){
                    fatherMap.put(node, node);
                    sizeMap.put(node, 1);
                }
            }
            private Node findHead(Node node){   //找到代表节点,并扁平优化集合结构
                Stack<Node> stack = new Stack<>();
                Node curr = node;
                Node parent = fatherMap.get(curr);
                while(curr != parent){
                    stack.push(node);
                    curr = parent;
                    parent = fatherMap.get(curr);
                }
                while (!stack.isEmpty()){
                    fatherMap.put(stack.pop(), parent);
                }
                return parent;
            }
            //如果说父节点一样,那么就是同一个集合
            public boolean isSameSet(Node a, Node b){
                return findHead(a) == findHead(b);
            }
    
            public void union(Node a, Node b){
                if(a == null || b == null){
                    return;
                }
                Node aHead = findHead(a);
                Node bHead = findHead(b);
                if(aHead != bHead){
                    int aSetSize = sizeMap.get(aHead);
                    int bSetSize = sizeMap.get(bHead);
                    if(aSetSize <= bSetSize){
                        fatherMap.put(aHead, bHead);
                        sizeMap.put(bHead, aSetSize + bSetSize);
                    }else{
                        fatherMap.put(bHead, aHead);
                        sizeMap.put(aHead, aSetSize + bSetSize);
                    }
                }
            }
        }
    }

    2. 例题

    (1)leetcode1202交换字符串中的元素

    交换字符串中的元素

    class Solution {
        public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
    //由于交换关系的传递性,pairs中的索引可以按照相连性分成组,这个组之内的所有字符可以直接排序
    //s = "dcab", pairs = [[0,3],[1,2],[0,2]],  0,1,2,3位置上的元素可以任意排序,因此是abcd
    //s = "dcab", pairs = [[0,3],[1,2]],    0,3一组任意排序,1,2一组任意排序
            if(pairs.size() == 0){
                return s;
            }
            int len = s.length();
            //构建并查集
            UnionFind union = new UnionFind(len);
            for(int i = 0; i < pairs.size(); i++){
                int index0 = pairs.get(i).get(0);
                int index1 = pairs.get(i).get(1);
                union.union(index0, index1);
            }
    
            Map<Integer, PriorityQueue<Character>> map = new HashMap<>();
            for(int i = 0; i < len; i ++){
                int head = union.findHead(i);
                if(!map.containsKey(head)){
                    map.put(head, new PriorityQueue<Character>());
                }
                map.get(head).add(s.charAt(i));
            }
            
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < len; i++){
                sb.append(map.get(union.findHead(i)).poll());
            }
            return sb.toString();
        }
    }
    class UnionFind{
        int[] parent;
        int[] rank;
        public UnionFind(int n){
            this.parent = new int[n];
            this.rank = new int[n];
            for(int i = 0; i < n; i++){
                parent[i] = i;
                rank[i] = 1;
            }
        }
        //加入同一个集合
        public void union(int x, int y){
            int rootx = findHead(x);
            int rooty = findHead(y);
            if(rootx == rooty){
                return;
            }
            if(rank[rootx] == rank[rooty]){
                parent[rootx] = rooty;
                rank[rooty] ++;
            }else if(rank[rootx] < rank[rooty]){
                parent[rootx] = rooty;
            }else{
                parent[rooty] = rootx;
            }
        }
        public int findHead(int x){
            if(x != parent[x]){
                parent[x] = findHead(parent[x]);
            }
            return parent[x];
        }
    }

    (2)leetcode200岛屿数量

    岛屿数量

    //并查集,不需要rank
    class Solution {
        public int numIslands(char[][] grid) {
            int rows = grid.length;
            int cols = grid[0].length;
            UnionFind union = new UnionFind(rows, cols);
            Set<Integer> set = new HashSet<>();
            for(int i = 0; i < rows; i++){
                for(int j = 0; j < cols; j++){
                    if(grid[i][j] == '1'){
                        if(i >= 1 && grid[i - 1][j] == '1'){//up
                            union.union(i, j, i - 1, j);
                        }
                        if(j >= 1 && grid[i][j - 1] == '1'){//left
                            union.union(i, j, i, j - 1);
                        }
                    }
                }
            }
            for(int i = 0; i < rows; i++){
                for(int j = 0; j < cols; j++){
                    if(grid[i][j] == '1'){
                        set.add(union.findHead(i, j));
                    }
                }
            }
            return set.size();
        }
    }
    class UnionFind{
        int[] parent;
        int rows;
        int cols;
        public UnionFind(int rows, int cols){
            this.rows = rows;
            this.cols = cols;
            this.parent = new int[rows * cols];
            for(int i = 0; i < rows; i ++){
                for(int j = 0; j < cols; j ++){
                    parent[cols * i + j] = cols * i + j;
                }
            }
        }
        //加入同一个集合
        public void union(int x1, int y1, int x2, int y2){
            int root1 = findHead(x1, y1);
            int root2 = findHead(x2, y2);
            if(root1 == root2){
                return;
            }
            parent[root1] = root2;
        }
        public int findHead(int x, int y){
            int index = this.cols * x + y;
            if(index != parent[index]){
                int row = parent[index] / cols;
                int col = parent[index] % cols;
                parent[index] = findHead(row, col);
            }
            return parent[index];
        }
    }

    (3)leetcode484冗余连接

    冗余连接

    class Solution {
        public int[] findRedundantConnection(int[][] edges) {
    // 并查集
            Set<Integer> set = new HashSet<>();
            for(int[] edge : edges){
                set.add(edge[0]);
                set.add(edge[1]);
            }
            int len = edges.length;
            boolean[] visited = new boolean[len];
            UnionFind uf = new UnionFind(set);
            for(int i = 0; i < len; i++){
                int[] edge = edges[i];
                int root1 = uf.findHead(edge[0]);
                int root2 = uf.findHead(edge[1]);
                if(root1 != root2){
                    visited[i] = true;
                    uf.union(root1, root2);
                }
            }
            for(int i = len - 1; i >= 0; i--){
                if(!visited[i]){
                    return edges[i];
                }
            }
            return new int[]{};
        }
    }
    class UnionFind{
        Map<Integer, Integer> parent;
        public UnionFind(Set<Integer> set){
            parent = new HashMap<>();
            for(int i : set){
                parent.put(i, i);
            }
        }
        public void union(int i, int j){
            int rooti = findHead(i);
            int rootj = findHead(j);
            parent.put(rootj, rooti);
        }
        public int findHead(int i){
            int father = parent.get(i);
            if(i != father){
                father = findHead(father);
            }
            return father;
        }
    }

    (4)leetcode1584连接所有点的最小费用

    连接所有点的最小费用

    // 并查集
    class Solution {
        public int minCostConnectPoints(int[][] points) {
            int n = points.length;//点的个数
            UnionFind uf = new UnionFind(n);
            List<Edge> edges = new ArrayList<>();
            for(int i = 0; i < n; i++){
                for(int j = i + 1; j < n; j++){
                    edges.add(new Edge(dist(points, i, j), i, j));
                }
            }
            // 把所有边按照长度从小到大进行排序
            Collections.sort(edges, (edge1, edge2) -> (edge1.len - edge2.len));
            int minCost = 0, edgeNum = 1;
            for(Edge edge : edges){
                int len = edge.len;
                int p1 = edge.x;
                int p2 = edge.y;
                if(uf.union(p1, p2)){
                    minCost += len;
                    edgeNum ++;
                    if(edgeNum == n){
                        break;
                    }
                }
            }
            return minCost;
        }
        public int dist(int[][] points, int i, int j){
            return Math.abs(points[i][0] - points[j][0]) + 
            Math.abs(points[i][1] - points[j][1]);
        }
    }
    class UnionFind{
        int[] parent;
        int[] rank;
        int n;
        public UnionFind(int n){
            this.n = n;
            parent = new int[n];
            rank = new int[n];
            Arrays.fill(rank, 1);
            for(int i = 0; i < n; i++){
                parent[i] = i;
            }
        }
        public int findHead(int i){
            if(i != parent[i]){
                parent[i] = findHead(parent[i]);
            }
            return parent[i];
        }
        // 这里设置为boolean是为了题目的需要
        public boolean union(int x, int y){
            int rootx = findHead(x);
            int rooty = findHead(y);
            if(rootx == rooty){
                return false;
            }
            if(rank[rootx] < rank[rooty]){
                // 交换
                int temp = rank[rootx];
                rank[rootx] = rank[rooty];
                rank[rooty] = temp;
            }
            // 扁平化树形结构
            rank[rootx] += rank[rooty];
            parent[rooty] = parent[rootx];
            return true;
        }
    }
    class Edge{
        int len, x, y;
        public Edge(int len, int x, int y){
            this.len = len;
            this.x = x;
            this.y = y;
        }
    }
  • 相关阅读:
    最清晰易懂的Mysql CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP区别
    yield再理解--绝对够透彻
    解决pyspider框架web预览框过小问题
    python中的可哈希与不可哈希
    python json.dumps 中的ensure_ascii 参数引起的中文编码问题
    LeetCode 297. 二叉树的序列化与反序列化 | Python
    LeetCode 1300. 转变数组后最接近目标值的数组和 | Python
    LeetCode 30. 串联所有单词的子串 | Python
    LeetCode 739. 每日温度 | Python
    LeetCode 128. 最长连续序列 | Python
  • 原文地址:https://www.cnblogs.com/lyuwalle/p/14273009.html
Copyright © 2011-2022 走看看