剑指 Offer II 116. 省份数量(547. 省份数量)
题目:
思路:
【1】深度优先搜索的方式
【2】广度优先搜索的方式
【3】并查集的方式
代码展示:
并查集的方式:
//时间1 ms击败87.49% //内存42.4 MB击败22.88% class Solution { public int findCircleNum(int[][] isConnected) { if (isConnected == null || isConnected.length == 0) { return 0; } int n = isConnected.length; UnionFind uf = new UnionFind(n); for(int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (isConnected[i][j] == 1) { uf.union(i, j); } } } return uf.getCount(); } class UnionFind { int root[]; int rank[]; int count; UnionFind(int size) { root = new int[size]; rank = new int[size]; count = size; for (int i = 0; i < size; i++) { root[i] = i; rank[i] = 1; } } int find(int x) { if (x == root[x]) { return x; } return root[x] = find(root[x]); } void union(int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX != rootY) { if (rank[rootX] > rank[rootY]) { root[rootY] = rootX; } else if (rank[rootX] < rank[rootY]) { root[rootX] = rootY; } else { root[rootY] = rootX; rank[rootX] += 1; } count--; } }; int getCount() { return count; } } } //时间1 ms击败87.49% //内存42.1 MB击败68.65% class Solution { public int findCircleNum(int[][] isConnected) { int cities = isConnected.length; int[] parent = new int[cities]; for (int i = 0; i < cities; i++) { parent[i] = i; } for (int i = 0; i < cities; i++) { for (int j = i + 1; j < cities; j++) { if (isConnected[i][j] == 1) { union(parent, i, j); } } } int provinces = 0; for (int i = 0; i < cities; i++) { if (parent[i] == i) { provinces++; } } return provinces; } public void union(int[] parent, int index1, int index2) { parent[find(parent, index1)] = find(parent, index2); } public int find(int[] parent, int index) { if (parent[index] != index) { parent[index] = find(parent, parent[index]); } return parent[index]; } }
广度优先搜索的方式:
//时间5 ms击败13.71% //内存42.1 MB击败76.4% //时间复杂度:O(n^2),其中 n 是城市的数量。需要遍历矩阵 isConnected 中的每个元素。 //空间复杂度:O(n),其中 n 是城市的数量。 //需要使用数组 visited 记录每个城市是否被访问过,数组长度是 n,广度优先搜索使用的队列的元素个数不会超过 n。 class Solution { public int findCircleNum(int[][] isConnected) { int cities = isConnected.length; boolean[] visited = new boolean[cities]; int provinces = 0; Queue<Integer> queue = new LinkedList<Integer>(); //这里其实和深度遍历一样每个节点都要检查一次 for (int i = 0; i < cities; i++) { if (!visited[i]) { queue.offer(i); //这里的意义便是跟递归一样,将能达到的地方又收集回队列,持续标记完 //相当于将递归变为循环 while (!queue.isEmpty()) { int j = queue.poll(); visited[j] = true; for (int k = 0; k < cities; k++) { if (isConnected[j][k] == 1 && !visited[k]) { queue.offer(k); } } } provinces++; } } return provinces; } }
深度优先搜索的方式:
//时间1 ms击败87.49% //内存42.4 MB击败21.93% //时间复杂度:O(n^2),其中 n 是城市的数量。需要遍历矩阵 n 中的每个元素。 //空间复杂度:O(n),其中 n 是城市的数量。 //需要使用数组 visited 记录每个城市是否被访问过,数组长度是 n,递归调用栈的深度不会超过 n。 class Solution { //利用递归,每遍历完一整串相连的节点,统计的岛屿数量才会加1 //辅以变量visited记录是否被遍历过(可以用int类型替代布尔类型,节约空间) public int findCircleNum(int[][] isConnected) { int cities = isConnected.length; boolean[] visited = new boolean[cities]; int provinces = 0; for (int i = 0; i < cities; i++) { if (!visited[i]) { dfs(isConnected, visited, cities, i); provinces++; } } return provinces; } public void dfs(int[][] isConnected, boolean[] visited, int cities, int i) { for (int j = 0; j < cities; j++) { if (isConnected[i][j] == 1 && !visited[j]) { visited[j] = true; dfs(isConnected, visited, cities, j); } } } }