There are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direct friend of C, then A is an indirect friend of C. And we defined a friend circle is a group of students who are direct or indirect friends.
Given a N*N matrix M representing the friend relationship between students in the class. If M[i][j] = 1, then the ithand jth students are direct friends with each other, otherwise not. And you have to output the total number of friend circles among all the students.
Example 1:
Input: [[1,1,0], [1,1,0], [0,0,1]] Output: 2 Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.
The 2nd student himself is in a friend circle. So return 2.
Example 2:
Input: [[1,1,0], [1,1,1], [0,1,1]] Output: 1 Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends,
so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1.
Note:
- N is in range [1,200].
- M[i][i] = 1 for all students.
- If M[i][j] = 1, then M[j][i] = 1.
這道題讓我們求朋友圈的個數,題目中對於朋友圈的定義是可以傳遞的,比如A和B是好友,B和C是好友,那么即使A和C不是好友,那么他們三人也屬於一個朋友圈。那么比較直接的解法就是 DFS 搜索,對於某個人,遍歷其好友,然后再遍歷其好友的好友,那么就能把屬於同一個朋友圈的人都遍歷一遍,同時標記出已經遍歷過的人,然后累積朋友圈的個數,再去對於沒有遍歷到的人在找其朋友圈的人,這樣就能求出個數。其實這道題的本質是之前那道題 Number of Connected Components in an Undirected Graph,其實許多題目的本質都是一樣的,就是看我們有沒有一雙慧眼能把它們識別出來:
解法一:
class Solution { public: int findCircleNum(vector<vector<int>>& M) { int n = M.size(), res = 0; vector<bool> visited(n, false); for (int i = 0; i < n; ++i) { if (visited[i]) continue; helper(M, i, visited); ++res; } return res; } void helper(vector<vector<int>>& M, int k, vector<bool>& visited) { visited[k] = true; for (int i = 0; i < M.size(); ++i) { if (!M[k][i] || visited[i]) continue; helper(M, i, visited); } } };
我們也可以用 BFS 來遍歷朋友圈中的所有人,解題思路和上面大同小異,參見代碼如下:
解法二:
class Solution { public: int findCircleNum(vector<vector<int>>& M) { int n = M.size(), res = 0; vector<bool> visited(n, false); queue<int> q; for (int i = 0; i < n; ++i) { if (visited[i]) continue; q.push(i); while (!q.empty()) { int t = q.front(); q.pop(); visited[t] = true; for (int j = 0; j < n; ++j) { if (!M[t][j] || visited[j]) continue; q.push(j); } } ++res; } return res; } };
下面這種解法叫聯合查找 Union Find,也是一種很經典的解題思路,在之前的兩道道題 Graph Valid Tree 和 Number of Connected Components in an Undirected Graph 中也有過應用,核心思想是初始時給每一個對象都賦上不同的標簽,然后對於屬於同一類的對象,在 root 中查找其標簽,如果不同,那么將其中一個對象的標簽賦值給另一個對象,注意 root 數組中的數字跟數字的坐標是有很大關系的,root 存的是屬於同一組的另一個對象的坐標,這樣通過 getRoot 函數可以使同一個組的對象返回相同的值,參見代碼如下:
解法三:
class Solution { public: int findCircleNum(vector<vector<int>>& M) { int n = M.size(), res = n; vector<int> root(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 (M[i][j] == 1) { int p1 = getRoot(root, i); int p2 = getRoot(root, j); if (p1 != p2) { --res; root[p2] = p1; } } } } return res; } int getRoot(vector<int>& root, int i) { while (i != root[i]) { root[i] = root[root[i]]; i = root[i]; } return i; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/547
類似題目:
Number of Connected Components in an Undirected Graph
參考資料:
https://leetcode.com/problems/friend-circles/
https://leetcode.com/problems/friend-circles/discuss/101440/c-bfs
https://leetcode.com/problems/friend-circles/discuss/101338/Neat-DFS-java-solution
https://leetcode.com/problems/friend-circles/discuss/101387/Easy-Java-Union-Find-Solution
