题目
有 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]
解题
方法一:DFS
思路
遍历所有城市,对于每个城市,如果该城市尚未被访问过,则从该城市开始深度优先搜索,通过矩阵 isConnected
得到与该城市直接相连的城市有哪些,这些城市和该城市属于同一个连通分量,然后对这些城市继续深度优先搜索,直到同一个连通分量的所有城市都被访问到,即可得到一个省份。
遍历完全部城市以后,即可得到连通分量的总数,即省份的总数。
代码
class Solution {
private int cities;
private boolean[] visited;
private int provinces = 0;
public int findCircleNum(int[][] isConnected) {
cities = isConnected.length;
visited = new boolean[cities];
for (int i = 0; i < cities; i++) {
if (visited[i]) continue;
dfs(i, isConnected);
provinces++;
}
return provinces;
}
public void dfs(int i, int[][] isConnected) {
for (int j = 0; j < cities; j++) {
if (visited[j] || isConnected[i][j] == 0) continue;
visited[j] = true;
dfs(j, isConnected);
}
}
}
方法二:并查集
思路
图的连通分量:
-
无向图G的极大连通子图称为G的连通分量( Connected Component)。任何连通图的连通分量只有一个,即是其自身,非连通的无向图有多个连通分量。
-
简单举例来说:
该图连通分量为 1
该图连通分量为 3
并查集的基本概念(借鉴):
- 并查集是一种数据结构。
- 并查集这三个字,一个字代表一个意思:
- 并(Union): 合并
- 查(Find): 查找
- 集(Set): 这是一个以字典为基础的数据结构,它的基本功能是合并集合中的元素,查找集合中的元素
- 并查集的典型应用是有关连通分量的问题。
- 并查集解决单个问题(添加,合并,查找)的时间复杂度都是 $O(1)$。
- 因此,并查集可以应用到在线算法中。
并查集的实现:
// 并查集跟树有些类似,只不过它跟树是相反的。
// 在树中,每个节点会记录它的子节点。
// 而在并查集中,每个节点会记录它的父节点。
class UnionFind {
private Map<Integer,Integer> nodeToRoot;
public UnionFind() {
nodeToRoot = new HashMap<Integer, Integer>();
}
// 初始化
// 当把一个新节点添加到并查集中,它的父节点应该为空
public void add(int x) {
if (!nodeToRoot.containsKey(x)) nodeToRoot.put(x, null);
}
// 如果发现两个节点是连通的,那么就要把他们合并,也就是他们的祖先是相同的。
// 这里究竟把谁当做父节点一般是没有区别的。
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP != rootQ) nodeToRoot.put(rootP, rootQ);
}
// 查找祖先,如果祖先不是初始化的 null 就一直迭代
public int find(int n) {
while (nodeToRoot.get(n) != null) {
nodeToRoot.put(n, find(nodeToRoot.get(n)));
n = nodeToRoot.get(n);
}
return n;
}
}
故本期其实就是求 isConnected
这个邻接矩阵表示的图的连通分量为多少,很适合用并查集直接求解。已经知道了节点的数量 isConnected.length
就可以使用数组代替字典实现并查集。
代码
class Solution {
private int[] nodeToRoot;
public int findCircleNum(int[][] isConnected) {
int cities = isConnected.length;
nodeToRoot = new int[cities];
for (int i = 0; i < cities; i++) nodeToRoot[i] = i;
for (int i = 0; i < cities; i++) {
for (int j = i + 1; j < cities; j++) {
if (isConnected[i][j] == 1) union(i, j);
}
}
int provinces = 0;
for (int i = 0; i < cities; i++) {
if (i == nodeToRoot[i]) provinces++;
}
return provinces;
}
private void union(int p, int q) {
nodeToRoot[find(p)] = find(q);
}
private int find(int n) {
if (n != nodeToRoot[n]) nodeToRoot[n] = find(nodeToRoot[n]);
return nodeToRoot[n];
}
}
评论区