【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:
输入:[[5,4,5],[1,2,6],[7,4,6]]
输出:4
解释:
得分最高的路径用黄色突出显示。
示例 2:
输入:[[2,2,1,2,2,2],[1,2,2,2,1,2]]
输出:2
示例 3:
输入:[[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)); }