问题来源
此题来源于LeetCode547. Friend Circles,主要运用了并查集(union find)、广度优先遍历(bfs)和深度优先遍历(bfs)三种方法解决。
问题简述
给定一个
N×N
的矩阵
M
表示了
比如:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
解释:第0和第1个人是直接朋友,他们构成1个朋友圈;第2个人自己构成一个朋友圈,所以返回2。
又比如:
输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
解释:第0和第1个人是直接朋友,第1和第2个人是直接朋友,所以第0和第2个人是间接朋友,这3个人构成了1个朋友圈,所以返回1。
值得注意的是:
- 总是有
M[i][j]=M[j][i] - 总是有 M[i][i]=1
解决方案
利用union find解决
此处的并查集用到了路径压缩的优化。
class Solution { private: vector<int> vec; int sz; private: void Initialize(int count){ vec = vector<int>(count); sz = count; for (int i = 0; i < count; i++) vec[i] = i; } int findRoot(int p){ assert(p >= 0 && p < sz); while (vec[p] != p){ //路径压缩 vec[p] = vec[vec[p]]; p = vec[p]; } return vec[p]; } void unionNode(int p, int q){ assert(p >= 0 && p < sz && q >= 0 && q < sz); int pRoot = findRoot(p); int qRoot = findRoot(q); if (pRoot != qRoot) vec[pRoot] = qRoot; } public: int findCircleNum(vector<vector<int>>& M) { int m = M.size(); if (0 >= m) return 0; Initialize(m); for (int i = 0; i < m; i++){ for (int j = i + 1 ; j < m; j++){ if (M[i][j] == 1) unionNode(i, j); } } int res = 0; for (int i = 0; i < m; i++) if (vec[i] == i) res++; return res; } };
利用bfs解决
由于问题的特殊性,每次都只要把对角线上的元素放到队列当中即可。
class Solution { private: int sz; private: void bfs(vector<vector<int>> &M, int x){ queue<int> q; q.push(x); while(!q.empty()){ int newX = q.front(); q.pop(); M[newX][newX] = 0; for (int i = 0; i < sz; i++){ if (1 == M[newX][i]){ M[newX][i] = 0; M[i][newX] = 0; if (1 == M[i][i]) q.push(i); } } } return; } public: int findCircleNum(vector<vector<int>>& M) { int m = M.size(); if (0 >= m) return 0; sz = m; int res = 0; for (int i = 0; i < m; i++){ if (1 == M[i][i]){ bfs(M,i); res++; } } return res; } };
利用dfs解决
dfs与bfs的思路大同小异,但元素过多可能会造成栈溢出。
class Solution { private: int sz; private: void dfs(vector<vector<int>> &M, int x, int y){ if (0 == M[x][y]) return; M[x][y] = 0; for (int i = 0; i < sz; i++){ if (1 == M[x][i]){ M[x][i] = 0; M[i][x] = 0; dfs(M, i, i); } } return; } public: int findCircleNum(vector<vector<int>>& M) { int m = M.size(); if (0 >= m) return 0; sz = m; int res = 0; for (int i = 0; i < m; i++){ if (1 == M[i][i]){ dfs(M,i,i); res++; } } return res; } };
结束语
以上三种方法最快的是并查集(union find),bfs和dfs的速度差不多,三者也许都还有优化的余地~