思路一:使用深度优先搜索,递归。为每一个节点设置一个标志表示是否已经被访问;每次遍历矩阵的一行,若该行之前未被访问过,则进行dfs。在dfs中首先将该点标记为已经访问过,并且遍历节点该行,若有值为一则递归dfs。最终一个节点之前要是被访问过,就不满足条件,res也不会增加。
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size(), res = 0;
vector<bool> visited(n, false);
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(isConnected, i, visited);
++res;
}
}
return res;
}
void dfs(vector<vector<int>>& isConnected, int i, vector<bool>& visited) {
visited[i] = true;
for (int k = 0; k < isConnected.size(); k++) {
if (isConnected[i][k] == 1 && !visited[k]) {
dfs(isConnected, k, visited);
}
}
}
};
思路二:使用并查集的概念,一开始每个节点都是单独一类,然后逐渐合并。具体是利用了树,每个节点找到他的根节点,如果和前者根节点相同,则合并,且省份减一。
class Solution {
public:
vector<int> roots; // 保存每个节点的根节点,根节点相同的节点即属于同一类。
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size(), ans = n;
init(n);
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (isConnected[i][j] == 1 && merge(i, j)) {
ans--;
}
}
}
return ans;
}
void init(int n) { // 初始化每个节点都是单独一类,所以他们的根节点就是他们本身。
roots.resize(n);
for (int i = 0; i < n; ++i) {
roots[i] = i;
}
}
int find(int x) {
int temp = x;
while (roots[temp] != temp) { // 第一个循环用于找到该节点的根节点。
temp = roots[temp];
}
while (roots[x] != x) { // 第二个循环用于把每次合并后,把子树上的每点的roots都直接指向根节点。从而使树深度趋近于二,不至于深度过深,使复杂度达到O(n)。
int t = roots[x];
roots[x] = temp;
x = t;
}
return x;
}
bool merge(int u, int v) {
int ru = find(u);
int rv = find(v);
if (ru == rv) { // 根节点相同则已经合并完成了,不用再次合并减少集合数量。
return false;
}
roots[ru] = rv;
return true;
}
};