并查集的基本思想与算法

并查集

用途

  • 解决连接问题
  • 解决路径问题

并查集的基本操作

union:两个元素之间建立连接

find:找到某个元素所连接的根元素

基本思想:用树来表示彼此连接的元素,当两个元素连接时,就将它们所在的树的根节点相互连接

基本代码

#include <cassert>

using namespace std;

//集合以根节点彼此连接
namespace UF2{
    class UnionFind{
    private:
        int* parent;
        int count;
    public:
        UnionFind(int count){
            parent = new int[count];
            this->count = count;
            for(int i=0;i<count;i++)
                parent[i] = i;
        }
        ~UnionFind(){
            delete[] parent;
        }

        int find(int p){
            assert(p>=0&&p<count);
            while(p!=parent[p])
                p=parent[p];
            return p;
        }

        bool isConnected(int p,int q){
            return find(p) == find(q);
        }

        void unionElements(int p,int q){
            int pRoot = find(p);
            int qRoot = find(q);
            if(pRoot==qRoot)
                return;
            parent[pRoot]=qRoot;
        }
    };
}

优化思路

第一种

将元素较多的树的根节点作为合并后的新树的根节点,生成新树

作用:较大的树一般会有更大的高度,合并后可以减少新树的高度,提高find时的效率


//基于size(基于树的大小)的优化
namespace UF3
{
class UnionFind
{
private:
    int* parent;
    int* sz;
    int count;
public:
    UnionFind(int count)
    {
        parent = new int[count];
        sz = new int[count];
        this->count = count;
        for(int i=0; i<count; i++)
        {
            parent[i] = i;
            sz[i] = 1;
        }
    }
    ~UnionFind()
    {
        delete[] parent;
    }

    int find(int p)
    {
        assert(p>=0&&p<count);
        while(p!=parent[p])
            p=parent[p];
        return p;
    }

    bool isConnected(int p,int q)
    {
        return find(p) == find(q);
    }

    void unionElements(int p,int q)
    {
        int pRoot = find(p);
        int qRoot = find(q);
        if(pRoot==qRoot)
            return;
        if(sz[pRoot]<sz[qRoot])
        {
            parent[pRoot] = qRoot;
            sz[qRoot] += sz[pRoot];
        }
        else
        {
            parent[qRoot] = pRoot;
            sz[pRoot] += sz[qRoot];
        }
    }
};
}

第二种

将高度较高的树的根节点作为新树的根节点,生成新树

#ifndef INC_04_OPTIMIZE_BY_SIZE_UNIONFIND5_H
#define INC_04_OPTIMIZE_BY_SIZE_UNIONFIND5_H

#include <cassert>

using namespace std;

//并查集操作的复杂度近乎为O(1)
// 我们的第五版Union-Find
//基于rank(基于树的层次)的优化
namespace UF5
{
class UnionFind
{
private:
    int* parent;
    int* rank; //rank[i]表示以i为根的集合所表示的树的层数
    int count;
public:
    UnionFind(int count)
    {
        parent = new int[count];
        rank = new int[count];
        this->count = count;
        for(int i=0; i<count; i++)
        {
            parent[i] = i;
            rank[i] = 1;
        }
    }
    ~UnionFind()
    {
        delete[] parent;
    }

    int find(int p)
    {
        assert(p>=0&&p<count);
        //路径压缩
        //第一版
        //      while(p!=parent[p])
        //     {
        //         parent[p]=parent[parent[p]];
        //         p = parent[p];

        //  }
        //return p;
        //第二版,压缩到高度为2
        //理论上比第一版更有,实际运行情况不一定
        if(p !=parent[p])
          parent[p] = find(parent[p]);
        return parent[p];
    }

    bool isConnected(int p,int q)
    {
        return find(p) == find(q);
    }

    void unionElements(int p,int q)
    {
        int pRoot = find(p);
        int qRoot = find(q);
        if(pRoot==qRoot)
            return;
        if(rank[pRoot]<rank[qRoot])
        {
            parent[pRoot] = qRoot;
        }
        else if(rank[qRoot] < rank[pRoot])
        {
            parent[qRoot] = pRoot;
        }
        else
        {
            parent[pRoot] = qRoot;
            rank[qRoot]+=1;
        }
    }
};
}
#endif

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值