链接:https://leetcode-cn.com/problems/number-of-provinces/
有 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]
思路:是一个很明显的并查集问题,开始时是n个独立的集合,每合并一次,集合数就减少1。我们用路径压缩进行优化(增加按秩合并也可以,但是如果时间不是卡的特别死,按秩合并的优化力度有限,反而会提高代码复杂度)。
class Solution {
public:
int Father[210];
int Find(int x){
if( x != Father[x] ){
return Father[x] = Find(Father[x]);
}
return Father[x];
}
void Union(int A,int B,int &Single){
int FA = Find(A), FB = Find(B);
if( FA != FB ){
Father[FA] = FB;
-- Single;//每成功合并一次 集合数就少一
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size(), i, j, Single = n;
for(i = 0; i < n; ++ i){
Father[i] = i;
}
for(i = 0; i < n; ++ i){
for(j = 0; j < n; ++ j){
if( isConnected[i][j] ){
Union(i, j, Single);
}
}
}
return Single;
}
};
增加按秩合并版:
class Solution {
public:
int Father[210];
int Rank[210];
int Find(int x){
if( x != Father[x] ){
return Father[x] = Find(Father[x]);
}
return Father[x];
}
void Union(int A,int B,int &Single){
int FA = Find(A), FB = Find(B);
if( FA != FB ){
if(Rank[FA] < Rank[FB]){
Father[FA] = FB;
}else if(Rank[FA] == Rank[FB]){
Father[FA] = FB;
++ Rank[FB];
}else{
Father[FB] = FA;
}
-- Single;//每成功合并一次 集合数就少一
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size(), i, j, Single = n;
memset(Rank,0,sizeof(Rank));
for(i = 0; i < n; ++ i){
Father[i] = i;
}
for(i = 0; i < n; ++ i){
for(j = 0; j < n; ++ j){
if( isConnected[i][j] ){
Union(i, j, Single);
}
}
}
return Single;
}
};
这种写法是为了保证Rank正确反映树的高度,但同时过于复杂的if-else语句是会降低程序执行性能的,如果我们不需要保证Rank的正确性,则可以:
class Solution {
public:
int Father[210];
int Rank[210];
int Find(int x){
if( x != Father[x] ){
return Father[x] = Find(Father[x]);
}
return Father[x];
}
void Union(int A,int B,int &Single){
int FA = Find(A), FB = Find(B);
if( FA != FB ){
if(Rank[FA] < Rank[FB]){
Father[FA] = FB;
++ Rank[FB];
}else{
Father[FB] = FA;
++ Rank[FA];
}
-- Single;//每成功合并一次 集合数就少一
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size(), i, j, Single = n;
memset(Rank,0,sizeof(Rank));
for(i = 0; i < n; ++ i){
Father[i] = i;
}
for(i = 0; i < n; ++ i){
for(j = 0; j < n; ++ j){
if( isConnected[i][j] ){
Union(i, j, Single);
}
}
}
return Single;
}
};