并查集

主要内容

本文主要记录并查集的基本实现方法,并逐步将一些例题填充到文章中。

并查集能做什么

并查集可以:
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);
}

例题

LeetCode 547 朋友圈

班上有 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。

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;
};

LeetCode 130 被围绕的区域

给定一个二维的矩阵,包含 '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;
};

posted @ 2018-09-15 21:00 Smalldy 阅读(...) 评论(...) 编辑 收藏