题目
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
题解
1、并查集
并查集有两个操作,find操作用于查找该节点属于哪个圈子,union操作合并圈子。
并查集初始时每个节点都是一个圈子,每个节点的圈子都指向自己,每次union合并操作,会把一个圈子a的根节点指向另一个圈子b的根节点,这样圈子a的所有节点的根节点也会跟着指向圈子b的根节点,达到合并的目的。
这里主要是因为圈子a中其他节点的根节点本来就不是他自己,因此在最后计算的时候不会看作省份,只有根节点是本身的节点才会看作是一个省份,因此当a圈和b圈相连,修改a圈根节点的指向,就可以保证ab圈的节点中只有一个是指向本身的。
class Solution {
// 寻找树根
int Find(int[] root, int index){
if(root[index] != index){ //说明当前节点的根不是自己,需要往上找
return Find(root, root[index]);
}
return root[index]; // 节点的根是自己,找到了根
}
void Union(int[] root, int i, int j){
// 将一个根节点指向另一个根节点
root[Find(root, i)] = Find(root, j);
}
// 初始每个节点的根指向自己,然后查找相连的,将相连的合并指向同一个根节点
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
int[] root = new int[n];
// 每个节点的根初始指向自己
for(int i = 0; i < n; i++){
root[i] = i;
}
// 查并集
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
if(isConnected[i][j] == 1){ //两个城市相连
Union(root, i, j);
}
}
}
// 计数,有多少个节点的根还是自己,那么就有多少省份
int count = 0;
for(int i = 0; i < n; i++){
if(root[i] == i) count++;
}
return count;
}
}
2、DFS
设置城市访问状态数组visited,初始值均为0。
对每个节点通过深度搜索寻找与其直接或间接连通的节点。
遍历所有节点,对于直接连通的节点 t 将其访问状态改为1,然后从节点 t 出发寻找连通的节点。
class Solution {
// 采用dfs方法
public int findCircleNum(int[][] isConnected) {
// 使用visited数组来表明是否访问过,若没有访问过,则结果+1,然后对该节点进行深搜
int count = 0;
int n = isConnected.length;
int[] visited = new int[n];
for(int i = 0; i < n; i++){
if(visited[i] == 0){
count++;
dfs(isConnected, visited, i); // 深搜节点i的连通节点
}
}
return count;
}
void dfs(int[][] isConnected, int[] visited, int index){
for(int j = 0; j < isConnected.length; j++){
if(isConnected[index][j] == 1 && visited[j] != 1){ //连通并且没访问过
visited[j] = 1; // 进入连通但未访问过的节点j,修改访问状态
dfs(isConnected, visited, j); //从j出发开始遍历,寻找连通节点
}
}
}
}