题目描述:
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
示例 2:
输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
注意:
- N 在[1,200]的范围内。
- 对于所有学生,有M[i][i] = 1。
- 如果有M[i][j] = 1,则有M[j][i] = 1。
思路分析:
思路1: 一开始以为只需要求图的连通分量个数就好了,就仿照之前岛屿的数量那道题做,利用dfs完成。结果出现了样例错误才知道这道题的真正题意。将原来的代码进行修改,这里的visited数组只需要是一维,对应每个人是否被访问过。对于每一个未被访问的人,进行dfs。dfs函数中,首先标记当前人被访问,接下来遍历他的朋友,若未被访问,则逐层深度访问其朋友。
思路2: 利用并查集实现。首先有n个对应其朋友圈编号为其本身,此时总的朋友圈数为人的总数。接下来遍历每个关系对,若i和j是朋友,分别获取i和j的朋友圈编号,若不一致,将i的朋友圈编号改为j,即合并两个人的朋友圈,朋友圈总数减1。遍历完成后,所得的朋友圈数即为总数。
代码:
1 class Solution { 2 public: 3 void dfs(vector<vector<int>>& M, vector<int>& visited, int idx) 4 { 5 visited[idx] = 1; 6 for(int j=0; j<M.size(); j++) 7 { 8 if(idx == j) 9 continue; 10 if(visited[j] == 0 && M[idx][j]) 11 { 12 dfs(M, visited, j); 13 } 14 } 15 } 16 int findCircleNum(vector<vector<int>>& M) { 17 if(M.size() == 0) 18 return 0; 19 vector<int> visited(M.size(), 0); 20 int cnt = 0; 21 for(int i=0; i<M.size(); i++) 22 { 23 if(visited[i] == 0 ) 24 { 25 cnt++; 26 dfs(M, visited, i); 27 } 28 } 29 return cnt; 30 } 31 };
思路2:
1 class Solution { 2 public: 3 int find(vector<int>& pre, int x) 4 { 5 return pre[x] == x ? x:find(pre, pre[x]); 6 } 7 int findCircleNum(vector<vector<int>>& M) { 8 if(M.size() == 0) 9 return 0; 10 vector<int> pre(M.size(), 0); 11 for(int i=0; i<M.size(); i++) 12 pre[i] = i; 13 int group = M.size(); 14 for(int i=0; i<M.size(); i++) 15 { 16 for(int j=0; j<M[i].size(); j++) 17 { 18 if(i!=j && M[i][j]) 19 { 20 int x1 = find(pre, i); 21 int x2 = find(pre, j); 22 if(x1 != x2) 23 { 24 pre[x1] = x2; 25 group--; 26 } 27 } 28 } 29 } 30 return group; 31 } 32 };