547. 朋友圈

难度中等255

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例 1:

1
2
3
4
5
6
7
输入: 
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。

示例 2:

1
2
3
4
5
6
输入: 
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。

注意:

  1. N 在[1,200]的范围内。
  2. 对于所有学生,有M[i][i] = 1。
  3. 如果有M[i][j] = 1,则有M[j][i] = 1。

通过次数47,496

提交次数83,180

并查集版本

改了好久,主要是要改unite函数的$if(roota==rootb)return$ ;这一句;之前用的是$f[a]==f[b]$做的判断,实际上不行;因为这里的f[a]==f[b]发生在路径压缩之前;

还有就是find函数的条件是$x==f[x]$ 别搞错了;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Solution {
public:
struct dis_set{
int n;
//int f[205];
vector<int>f;
dis_set(int x){
// this->n=205;
//f.resize(this->n);
//iota(f.begin(),f.end(),0);
n=x;
f.resize(x+1);
for(int i=0;i<this->n;i++){
f[i]=i;
}
}
int find(int x){
if(x==f[x]){
return x;
}

f[x]=find(f[x]);
return f[x];

}
void unite(int a,int b){
int roota=find(a);
int rootb=find(b);
if(roota==rootb)return ;
this->n--;
f[roota]=rootb;

}
int count(){
return n;
}
};
int findCircleNum(vector<vector<int>>& M) {
//我感觉可以并查集,然后对每一个同学查询?
if(M.size()==0)
return 0;
int n=M[0].size();
struct dis_set cur(n);
for(int i=0;i<M.size();++i){
for(int j=0;j<M[0].size();++j){
if(M[i][j]==1){
cur.unite(i,j);
}
}
}
return cur.count();
}

};

dfs版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
vector<int>vis;

void dfs(int id,vector<vector<int>>&M){
if(vis[id]==1)return;
vis[id]=1;
for(int i=0;i<M[id].size();++i){
if(M[id][i]==1){

dfs(i,M);

}
}
}
int findCircleNum(vector<vector<int>>& M) {
//我感觉可以并查集,然后对每一个同学查询?
//实际上还可以用dfs做,用vis记录已经访问过的结点;

if(M.size()==0)
return 0;
int n=M[0].size();
int cnt=0;
vis.resize(n);
// iota(vis.begin(),vis.end(),0); mgj iota的含义是填充0,1,2,3,4...不是所有的填充同一个数;
for(int i=0;i<n;i++){
vis[i]=0;
}
for(int i=0;i<M.size();++i){
if(vis[i]==0){
dfs(i,M);
cnt++;
}
}
return cnt;



}

};

bfs 版本的解法:

注意vis[j]=1那句,很重要;

image-20200617203349985
image-20200617203349985
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
public:

int findCircleNum(vector<vector<int>>& M) {
//我感觉可以并查集,然后对每一个同学查询?
//实际上还可以用dfs做,用vis记录已经访问过的结点;
//还可以用bfs做;同样用vis记录没有访问过的结点;

if(M.size()==0)
return 0;
int n=M[0].size();
int cnt=0;
queue<int>Q;
vector<int>vis(n,0);
for(int i=0;i<M.size();++i){
if(vis[i]!=1){
Q.push(i);
while(!Q.empty()){
int top=Q.front();
Q.pop();
vis[top]=1;
for(int j=0;j<M[top].size();++j){
if(M[top][j]==1&&vis[j]==0){
Q.push(j);
vis[j]=1;//这句很重要,如果没有这句会慢很多;
}
}
}
cnt++;
}
}
return cnt;



}

};