本文主要记录并查集的基本实现方法,并逐步将一些例题填充到文章中。ide
并查集能够:
1. 合并集合
2. 查找两个元素是否在同一个集合内
3. 集合数量
4. 肯定元素属于哪一个集合。函数
class UF { public: UF(); UF(int N); ~UF(); bool isConnected(int p, int q); int findRoot(int p);//查找根节点 主要函数 void unionSet(int p, int q);//合并集合 主要函数 int getSetCount(); int getElemCount(); private: std::vector<int> m_parent; std::vector<int> m_rank; int m_setCount; //集合个数 int m_elemCount; //元素个数 }; UF::UF() { m_setCount = 0; m_elemCount = 0; } UF::UF(int N): m_setCount(N), m_elemCount(N) { m_parent.resize(N); m_rank.resize(N); for(int i = 0; i < N; ++i) { m_parent[i] = i; m_rank[i] = 1; } } UF::~UF() { } int UF::findRoot( int p ) { if(p != m_parent[p]) { m_parent[p] = findRoot(m_parent[p]); } return m_parent[p]; } void UF::unionSet(int p, int q ) { int rootOfP = findRoot(p); int rootOfQ = findRoot(q); if( rootOfP == rootOfQ ) return ; if(m_rank[rootOfP] > m_rank[rootOfQ]) { m_parent[rootOfQ] = rootOfP; } else { if(m_rank[rootOfP] == m_rank[rootOfQ]) { m_rank[rootOfQ]++; } m_parent[rootOfP] = rootOfQ; } m_setCount--; } bool UF::isConnected( int p, int q) { return findRoot(p) == findRoot(q); }
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具备是传递性。若是已知 A 是 B 的朋友,B 是 C 的朋友,那么咱们能够认为 A 也是 C 的朋友。所谓的朋友圈,是指全部朋友的集合。code
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。若是M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,不然为不知道。你必须输出全部学生中的已知的朋友圈总数。leetcode
示例 1:get
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生本身在一个朋友圈。因此返回2。io
示例 2:class
输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,因此学生0和学生2也是朋友,因此他们三个在一个朋友圈,返回1。nio
注意:方法
N 在[1,200]的范围内。
对于全部学生,有M[i][i] = 1。
若是有M[i][j] = 1,则有M[j][i] = 1。集合
class Solution { public: int findCircleNum(vector<vector<int>>& M) { N = M.size(); count = N; parent.resize(count); rank.resize(count); for(int i = 0; i < N; i++) { parent[i] = i; rank[i] = 1; } for(int i = 0; i < N -1 ; ++i) { for(int j = i + 1; j < N; ++j) { if(M[i][j] == 1) { uni(i,j); } } } return count; } int root(int p) { if(p != parent[p]) { parent[p] = root(parent[p]); } return parent[p]; } void uni(int p, int q) { int proot = root(p); int qroot = root(q); if(proot == qroot) return ; if(rank[proot] > rank[qroot]) { parent[qroot] = proot; } else { if(rank[proot] == rank[qroot]) { rank[qroot]++; } parent[proot] = qroot; } count--; } int N; vector<int> parent; vector<int> rank; int count; };
给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。
找到全部被 'X' 围绕的区域,并将这些区域里全部的 'O' 用 'X' 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。若是两个元素在水平或垂直方向相邻,则称它们是“相连”的。
class Solution { public: void solve(vector<vector<char>>& board) { if(board.size()==0||board[0].size()==0)return; h = board.size(); w = board[0].size(); len = h*w; parent.resize(len); rank.resize(len); isSide.resize(len); for(int i = 0; i < len; i++) { int x = i/w; int y = i%w; parent[i] = i; rank[i] = 1; if(x == 0 || x == h-1 || y == 0 || y == w-1) { if(board[x][y] == 'O') isSide[i] = true; } else { isSide[i] = false; } } for(int i = 1; i < h; ++i) { for(int j = 1; j < w; ++j) { if(board[i][j-1] == board[i][j]) uni(i*w+j,i*w+j-1); if(board[i-1][j] == board[i][j]) uni(i*w+j,(i-1)*w+j); } } for(int i = 0; i < len; i++) { if(board[i/w][i%w] == 'O' && !isSide[root(i)]) board[i/w][i%w] = 'X'; } } int root(int p) { if(p != parent[p]) { parent[p] = root(parent[p]); } return parent[p]; } void uni(int p, int q) { int proot = root(p); int qroot = root(q); if(proot == qroot) return ; if(rank[proot] > rank[qroot]) { parent[qroot] = proot; } else { if(rank[proot] = rank[qroot]) { rank[qroot]++; } parent[proot] = qroot; } if(isSide[proot]) isSide[qroot] = true; if(isSide[qroot]) isSide[proot] = true; } vector<int> parent; vector<int> rank; vector<bool> isSide; int h; int w; int len; };