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

    【leetcode】547. 朋友圈 并查集

    题目

    班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

    给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

    示例 1:

    输入:
    [[1,1,0],
    [1,1,0],
    [0,0,1]]
    输出: 2
    说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
    第2个学生自己在一个朋友圈。所以返回2。

    547. 省份数量

    有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

    省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

    给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

    返回矩阵中 省份 的数量。

    示例 1:


    输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
    输出:2
    示例 2:


    输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
    输出:3
     

    提示:

    1 <= n <= 200
    n == isConnected.length
    n == isConnected[i].length
    isConnected[i][j] 为 1 或 0
    isConnected[i][i] == 1
    isConnected[i][j] == isConnected[j][i]
    通过次数165,040提交次数266,697

    来源:力扣(LeetCode)

    链接:https://leetcode-cn.com/problems/friend-circle
    链接:https://leetcode-cn.com/problems/number-of-provinces
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    思路

    并查集的裸题

      public static int findCircleNum2(int[][] isConnected) {
            int lens = isConnected.length;
            int[] parent = new int[lens];
            for (int i=0; i<lens; i++) {
                parent[i] = i;
            }
            for (int i=0; i<lens; i++) {
                for (int j=i+1; j<lens; j++) {
                    if (isConnected[i][j] ==1) {
                        union(parent, i, j);
                    }
                }
            }
            int circles = 0;
            for (int i=0; i<lens; i++) {
                if (parent[i] == i) {
                    circles++;
                }
            }
            return circles;
        }
    
        private static void union(int[] parent, int index1, int index2) {
            parent[find(parent, index1)] = find(parent, index2);
        }
    
        private static int find(int[] parent, int index) {
            if (parent[index] != index) {
                parent[index] = find(parent, parent[index]);
            }
            return parent[index];
        }

    解题思路二 深度搜索

        public static int findCircleNum(int[][] isConnected) {
            int lens = isConnected.length;
            boolean[] hasVisited=new boolean[lens];
            int nums = 0;
    
            for(int i=0; i<lens; i++) {
                if (hasVisited[i] == false) {
                    hasVisited[i] =true;
                    nums++;
                    dfs(isConnected, i, hasVisited);
                }
            }
            return nums;
        }
    
        private static void dfs(int[][] isConnected, int index, boolean[] hasVisited) {
            for (int i=0; i<isConnected[0].length; i++) {
                if (isConnected[i][index] == 1 && hasVisited[i] == false) {
                    hasVisited[i] = true;
                    dfs(isConnected, i, hasVisited);
                }
            }
        }

    200. 岛屿数量

    给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

    岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

    此外,你可以假设该网格的四条边均被水包围。

    示例 1:

    输入:grid = [
    ["1","1","1","1","0"],
    ["1","1","0","1","0"],
    ["1","1","0","0","0"],
    ["0","0","0","0","0"]
    ]
    输出:1
    示例 2:

    输入:grid = [
    ["1","1","0","0","0"],
    ["1","1","0","0","0"],
    ["0","0","1","0","0"],
    ["0","0","0","1","1"]
    ]
    输出:3
     

    提示:

    m == grid.length
    n == grid[i].length
    1 <= m, n <= 300
    grid[i][j] 的值为 '0' 或 '1'

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/number-of-islands
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    
    

    方法一:深度优先搜索

    class Solution {
        void dfs(char[][] grid, int r, int c) {
            int nr = grid.length;
            int nc = grid[0].length;
    
            if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
                return;
            }
    
            grid[r][c] = '0';
            dfs(grid, r - 1, c);
            dfs(grid, r + 1, c);
            dfs(grid, r, c - 1);
            dfs(grid, r, c + 1);
        }
    
        public int numIslands(char[][] grid) {
            if (grid == null || grid.length == 0) {
                return 0;
            }
    
            int nr = grid.length;
            int nc = grid[0].length;
            int num_islands = 0;
            for (int r = 0; r < nr; ++r) {
                for (int c = 0; c < nc; ++c) {
                    if (grid[r][c] == '1') {
                        ++num_islands;
                        dfs(grid, r, c);
                    }
                }
            }
    
            return num_islands;
        }
    }
    方法二:广度优先搜索
    class Solution {
        public int numIslands(char[][] grid) {
            if (grid == null || grid.length == 0) {
                return 0;
            }
    
            int nr = grid.length;
            int nc = grid[0].length;
            int num_islands = 0;
    
            for (int r = 0; r < nr; ++r) {
                for (int c = 0; c < nc; ++c) {
                    if (grid[r][c] == '1') {
                        ++num_islands;
                        grid[r][c] = '0';
                        Queue<Integer> neighbors = new LinkedList<>();
                        neighbors.add(r * nc + c);
                        while (!neighbors.isEmpty()) {
                            int id = neighbors.remove();
                            int row = id / nc;
                            int col = id % nc;
                            if (row - 1 >= 0 && grid[row-1][col] == '1') {
                                neighbors.add((row-1) * nc + col);
                                grid[row-1][col] = '0';
                            }
                            if (row + 1 < nr && grid[row+1][col] == '1') {
                                neighbors.add((row+1) * nc + col);
                                grid[row+1][col] = '0';
                            }
                            if (col - 1 >= 0 && grid[row][col-1] == '1') {
                                neighbors.add(row * nc + col-1);
                                grid[row][col-1] = '0';
                            }
                            if (col + 1 < nc && grid[row][col+1] == '1') {
                                neighbors.add(row * nc + col+1);
                                grid[row][col+1] = '0';
                            }
                        }
                    }
                }
            }
    
            return num_islands;
        }
    }
    方法三:并查集
    
    class Solution {
        class UnionFind {
            int count;
            int[] parent;
            int[] rank;
    
            public UnionFind(char[][] grid) {
                count = 0;
                int m = grid.length;
                int n = grid[0].length;
                parent = new int[m * n];
                rank = new int[m * n];
                for (int i = 0; i < m; ++i) {
                    for (int j = 0; j < n; ++j) {
                        if (grid[i][j] == '1') {
                            parent[i * n + j] = i * n + j;
                            ++count;
                        }
                        rank[i * n + j] = 0;
                    }
                }
            }
    
            public int find(int i) {
                if (parent[i] != i) parent[i] = find(parent[i]);
                return parent[i];
            }
    
            public void union(int x, int y) {
                int rootx = find(x);
                int rooty = find(y);
                if (rootx != rooty) {
                    if (rank[rootx] > rank[rooty]) {
                        parent[rooty] = rootx;
                    } else if (rank[rootx] < rank[rooty]) {
                        parent[rootx] = rooty;
                    } else {
                        parent[rooty] = rootx;
                        rank[rootx] += 1;
                    }
                    --count;
                }
            }
    
            public int getCount() {
                return count;
            }
        }
    
        public int numIslands(char[][] grid) {
            if (grid == null || grid.length == 0) {
                return 0;
            }
    
            int nr = grid.length;
            int nc = grid[0].length;
            int num_islands = 0;
            UnionFind uf = new UnionFind(grid);
            for (int r = 0; r < nr; ++r) {
                for (int c = 0; c < nc; ++c) {
                    if (grid[r][c] == '1') {
                        grid[r][c] = '0';
                        if (r - 1 >= 0 && grid[r-1][c] == '1') {
                            uf.union(r * nc + c, (r-1) * nc + c);
                        }
                        if (r + 1 < nr && grid[r+1][c] == '1') {
                            uf.union(r * nc + c, (r+1) * nc + c);
                        }
                        if (c - 1 >= 0 && grid[r][c-1] == '1') {
                            uf.union(r * nc + c, r * nc + c - 1);
                        }
                        if (c + 1 < nc && grid[r][c+1] == '1') {
                            uf.union(r * nc + c, r * nc + c + 1);
                        }
                    }
                }
            }
    
            return uf.getCount();
        }
    }

    1102. 得分最高的路径

    给你一个 R 行 C 列的整数矩阵 A。矩阵上的路径从 [0,0] 开始,在 [R-1,C-1] 结束。

    路径沿四个基本方向(上、下、左、右)展开,从一个已访问单元格移动到任一相邻的未访问单元格。

    路径的得分是该路径上的 最小 值。例如,路径 8 → 4 → 5 → 9 的值为 4 。

    找出所有路径中得分 最高 的那条路径,返回其 得分。

    示例 1:

     
    image.png

    输入:[[5,4,5],[1,2,6],[7,4,6]]
    输出:4
    解释:
    得分最高的路径用黄色突出显示。
    示例 2:

     
    image.png

    输入:[[2,2,1,2,2,2],[1,2,2,2,1,2]]
    输出:2
    示例 3:

     
    image.png

    输入:[[3,4,6,3,4],[0,2,1,1,7],[8,8,3,2,7],[3,2,4,9,8],[4,1,2,0,0],[4,6,5,4,3]]
    输出:3

    思路:单纯用贪心取四个方向最大值,得不到正确结果。
    dfs取所有的路径取min,超时。
    本题应用dijstra算法。通过heap来存所有可达点的值,按该值排序。



    作者:月下围城
    链接:https://www.jianshu.com/p/01f2aa3fa945
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
     
    import java.util.LinkedList;
    import java.util.List;
    
    public class MaximumMinimumPath {
        static class Node{
            int row;
            int col;
            int val;
    
            public Node(int row, int col,  int val) {
                this.row = row;
                this.col = col;
                this.val = val;
            }
        }
        static class UnionFind {
            private int[] parent;
            private int count;
            public UnionFind(int count) {
                this.count = count;
                this.parent = new int[count];
                for(int i=0; i<count; ++i){
                    parent[i] = i;
                }
            }
            private int find(int index) {
                assert (index>=0 && index <count);
                if (index != parent[index]) {
                    return find(parent[index]);
                }
                return index;
            }
            public void union (int index1, int index2) {
                int index1Root = find(index1);
                int index2Root = find(index2);
                if (index1Root != index2Root) {
                    parent[index1Root] = index2Root;
                }
            }
    
            public boolean isConnected(int index1, int index2) {
                return find(index1) == find(index2);
            }
        }
    
    
        public static int maximumMinimumPath(int[][] heights) {
    
            int row = heights.length;
            if (row == 0 ) {
                return 0;
            }
            int col = heights[0].length;
            if ( col == 0) {
                return 0;
            }
            int ans = Math.min(heights[0][0], heights[row-1][col-1]);
    
            List<Node> heap = new LinkedList<Node>();
    
            for(int i=0; i< row; ++i){
                for (int j= 0; j< col; ++j) {
                    heap.add(new Node(i, j, heights[i][j]));
                }
            }
            heap.sort((node1, node2)->( node2.val - node1.val));
            UnionFind unionFind = new UnionFind(row * col);
    
            int[][] directs = new int[][] {{0, 1},{0, -1},{-1, 0},{1, 0}};
            int[][] tag = new int[row][col];
            int end = row * col -1;
            for (int i=0 ; i<heap.size(); ++i) {
                Node node = heap.get(i);
                ans = Math.min(ans, node.val);
                tag[node.row][node.col] = 1;
                int rootIndex = node.row * col + node.col;
                for (int[] dir: directs) {
                    int tagRow = node.row + dir[0];
                    int tagCol = node.col + dir[1];
                    if (tagRow >= 0 && tagRow < row && tagCol >= 0 && tagCol < col && tag[tagRow][tagCol] == 1) {
                        int rootNindex = tagRow * col + tagCol;
                        unionFind.union(rootIndex, rootNindex);
                    }
                }
                if (unionFind.isConnected(0, end)){
                    break;
                }
            }
            return ans;
        }
    
        public static void main(String[] args) {
            int[][] heights = new int[][] {
                    {5,4,5},
                    {1,2,6},
                    {7,4,6}};
            System.out.println(maximumMinimumPath(heights));
        }
    }
     

    leetcode 1135. 最低成本联通所有城市

    想象一下你是个城市基建规划者,地图上有 N 座城市,它们按以 1 到 N 的次序编号。

    给你一些可连接的选项 conections,其中每个选项 conections[i] = [city1, city2, cost] 表示将城市 city1 和城市 city2 连接所要的成本。(连接是双向的,也就是说城市 city1 和城市 city2 相连也同样意味着城市 city2 和城市 city1 相连)。

    返回使得每对城市间都存在将它们连接在一起的连通路径(可能长度为 1 的)最小成本。该最小成本应该是所用全部连接代价的综合。如果根据已知条件无法完成该项任务,则请你返回 -1。

    示例 1:

    输入:N = 3, conections = [[1,2,5],[1,3,6],[2,3,1]]
    输出:6
    解释:
    选出任意 2 条边都可以连接所有城市,我们从中选取成本最小的 2 条。
    示例 2:

    输入:N = 4, conections = [[1,2,3],[3,4,4]]
    输出:-1
    解释:
    即使连通所有的边,也无法连接所有城市。

    提示:

    1 <= N <= 10000
    1 <= conections.length <= 10000
    1 <= conections[i][0], conections[i][1] <= N
    0 <= conections[i][2] <= 10^5
    conections[i][0] != conections[i][1]

    https://www.cnblogs.com/strengthen/p/11258422.html

    解法1:kruskal算法

    kruskal算法核心思想:
    1)初始阶段,每个点互不相识,各自为一个孤岛。
    2)以题设给定的“边”为入手,不断的通过整合边所连接两个点,让所有孤岛都连接到一起。
    3)利用贪心算法,选择cost小的边为起点,遍历所有的边。
    4)遍历的过程中,如果发现当前边所在的两个点在两个孤岛上,则将他们合并。这一步采用的并查集方法(即为不同的集合寻找father,father相同的节点,为同一个集合)。
    思想还是比较清晰和简洁,我没有去证明为什么第三步用贪心算法一定是可以的。。有兴趣的可以查阅资料再证明一下。

    import java.util.LinkedList;
    import java.util.List;
    
    public class MinimumCost {
        static class Node{
            int left;
            int right;
            int val;
    
            public Node(int row, int col,  int val) {
                this.left = row;
                this.right = col;
                this.val = val;
            }
        }
        static class UnionFind {
           private int[] parent;
           private int count;
    
           public UnionFind(int count) {
               this.count = count;
               this.parent = new int[count];
               for(int i=0; i<count; ++i){
                   parent[i] = i;
               }
           }
           public int find (int index) {
               if (parent[index] == index) {
                   return index;
               }else {
                   return find(parent[index]);
               }
           }
           public int union(Node node) {
               int index1Root = find(node.left);
               int index2Root = find(node.right);
               if (index1Root != index2Root) {
                   parent[index1Root] = index2Root;
                   return node.val;
               }
               return 0;
           }
        }
    
        public static int minimumCost(int N, int[][] connections) {
            int row = connections.length;
            List<Node> heap = new LinkedList<Node>();
    
            for(int i=0; i< row; ++i){
                heap.add(new Node(connections[i][0], connections[i][1], connections[i][2]));
            }
            heap.sort((node1, node2)->(node1.val- node2.val));
            UnionFind unionFind = new UnionFind(N+1);
    
            int cost =0;
            for(int i=0; i< heap.size(); ++i){
                Node node = heap.get(i);
                cost+=unionFind.union(node);
            }
            for(int i=2; i<= N; ++i){
                if (unionFind.find(i) != unionFind.find(i-1)) {
                    return -1;
                }
            }
    
            return cost;
        }
    
        public static void main(String[] args) {
            int N = 4;
            int[][] conections = {{1,2,5},{1,3,6},{2,3,1}, {2,4,10},{1,4,1}};
    
            System.out.println(minimumCost(N, conections));
        }
    }


        static class UnionFind {
            private int[] parent;
            private int count;
    
            public UnionFind(int count) {
                this.count = count;
                parent = new int[count];
                for (int i=0; i<count; i++) {
                    parent[i] = i;
                }
            }
            public int find(int index) {
                if (parent[index] == index) {
                    return index;
                } else {
                    return find(parent[index]);
                }
            }
            public boolean union(int index1, int index2) {
                int roota = find(index1);
                int rootb = find(index2);
                if (roota != rootb) {
                    parent[roota] = rootb;
                    return true;
                }
                return false;
            }
        }
    
        public static int minimumCost(int N, int[][] connections) {
            Arrays.sort(connections, (o1,o2)->(o1[2]-o2[2]));
            UnionFind unionFind = new UnionFind(N+1);
            int cost = 0;
            for (int i=0; i<connections.length; ++i) {
                Boolean isUnion = unionFind.union(connections[i][0], connections[i][1]);
                if (isUnion) {
                    cost += connections[i][2];
                }
            }
            for (int i=2; i<=N; ++i) {
                if (unionFind.find(i) != unionFind.find(i-1)) {
                    return -1;
                }
            }
            return cost;
        }
    
        public static void main(String[] args) {
            int N = 4;
            int[][] connections =  {{1,2,3},{3,4,4},{1,3,20},{2,4,6},{1,4,2}};
    
            System.out.println(minimumCost(N, connections));
        }
    
    


     
  • 相关阅读:
    Triangle
    Populating Next Right Pointers in Each Node II
    Populating Next Right Pointers in Each Node
    面试题之判断栈的入栈和出栈序列的合法性
    对称矩阵的压缩存储和输出
    栈的经典面试题之用两个栈实现一个队列
    C++的三大特性之一继承
    C++之类的析构函数
    malloc函数的底层实现你是否清楚
    【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园全面总结,再也不愁发博客了
  • 原文地址:https://www.cnblogs.com/yuluoxingkong/p/15426306.html
Copyright © 2011-2022 走看看