关于并查集的一些心得

并查集

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

假设有M个球,然后给出N条约束条件(每个条件以Ma,Mb表示ab两个球放在同一个盒子里),让你求需要多少个盒子,以及每个盒子里球的编号。

在数据规模比较小时,可以考虑用图来表示。每个连通子图里是一个盒子里的所有小球
但是在规模比较大的时候,图法非常低效。这时候需要考虑使用并查集

并查集的核心为:

  • 对两个已有集合的合并 (合并)
  • 获取一个元素所在的集合 (查找)
  • 初始化集合

查找:
如果用一个变量来唯一的标识集合,那么此变量的值就是查找的结果
我们把这个变量起名为parent
合并:
假设被合并的集合为V,合并到U上
合并时,需要修改V的parent的值。
为了提高效率,我们并不立刻修改V的所有元素的parent的值,而是修改元素e到V路径上的元素的parent的值
初始化:
将parent指向自身


class DS
{
public:
    DS(int x) //初始化
    {
        val = x;
        parent=this;
    }
    void contact(DS* b)//合并
    {
        DS* v = b->find();
        DS* u = this->find();
        v->parent = u;
    }
    DS* find() //查找
    {
        return this==parent?parent:parent=parent->find();
    }


    int val;
    DS* parent;
};

我们需要一个通过x获取DS的途径

map<int,DS*> forest;

插入关系

int a,b;

cin >> a >> b;
if(forest.count(a)<1)
{
    forest.insert(make_pair(a,new DS(a))); //如果不存在则初始化集合
}
if(forest.count(b)<1)
{
    forest.insert(make_pair(b,new DS(b)));//如果不存在则初始化集合
}
(forest.find(a)->second)->contact(forest.find(b)->second);

判断是否在一个集合

bool is_in_same_set(int a,int b) //判断是否在一个集合
{
    auto pair_a = forest.find(a);
    auto pair_b = forest.find(b);
    DS* parent_a = nullptr;
    DS* parent_b = nullptr;

    if(pair_a!=forest.end())
        parent_a = pair_a->second->find();
    if(pair_b!=forest.end())
        parent_b = pair_b->second->find();

    return (parent_a!=nullptr && parent_a==parent_b);
}

对于刚才那道小球的题目,上面的写法输出每个集合很不方便。

#include<bits/stdc++.h>

template<typename T,const bool _contain_dat> class DS
{
public:
    static std::map<T,DS<T,_contain_dat>*> mapper;
    const static bool pointer_only = !_contain_dat;
    DS(T x)
    {
        parent = this;

        if(!pointer_only)
        {
            S = new std::set<T>();
            S->insert(x);
        }

        mapper[x] = this;
    }
    ~DS()
    {
        if(!pointer_only)
        {
            delete S;
        }
    }

    static void add(T a,T b)//将两个元素所在的集合合并为一个集合
    {
        if(!contain(a))
            mapper[a] = new DS(a);
        if(!contain(b))
            mapper[b] = new DS(b);

        mapper[a]->contact(mapper[b]);
    }

    static bool contain(T x) //检查元素x是否存在某个集合中
    {
        return mapper.count(x)>0;
    }

    static bool sameSet(T a,T b)//判断两个元素是否属于同一集合
    {
        DS *ds_a = nullptr;
        DS *ds_b = nullptr;

        if(contain(a))
        {
            ds_a = mapper[a]->get_parent();
        }
        if(contain(b))
        {
            ds_b = mapper[b]->get_parent();
        }

        return ((ds_a!=nullptr)&&(ds_a==ds_b));
    }
    static void show_sets() //打印所有的集合
    {
        if(pointer_only)
            return;

        for(auto map_ele:mapper)
        {
            DS* ds = map_ele.second;
            if(ds->get_parent()==ds)
            {
                std::cout<<"{";

                for(auto ele:*(ds->get_set()))
                {
                    std::cout<<ele<<",";
                }
                std::cout<<"}"<<std::endl;
            }

        }
    }

    std::set<T>* get_set()
    {
        return this->get_parent()->S;
    }

private:
    void contact(DS* b) //将b加入当前集合
    {
        if(b==this)
            return;

        DS* u = this->get_parent();
        DS* v = b->get_parent();

        if(!pointer_only)
        {
            std::set<T>* uSet = u->get_set();
            std::set<T>* vSet = v->get_set();
            uSet->insert(vSet->begin(),vSet->end());
            delete vSet;
            //vSet = nullptr;
        }

        v->parent = u;
    }
    DS* get_parent()
    {
        return this==parent?parent:parent=parent->get_parent();
    }
    std::set<T>* S;
    DS* parent;
};

template<typename T,const bool dat> std::map<T,DS<T,dat>*> DS<T,dat>::mapper;


int main()
{

    unsigned m,b1,b2;
    std::cin >> m;
    while(m--)
    {
        std::cin >> b1 >> b2;
        DS<unsigned,true>::add(b1,b2);
    }

    DS<unsigned,true>::show_sets();

    return 0;
}

上面的代码增加了一个set<T>来存储具体的集合
下面是简单的测试:
输入

4
5 1
2 6
4 7
1 7

输出

{2,6,}
{1,4,5,7,}

PS:其实有更简洁的方法实现,不过思路也类似,具体来说是使用一个
map<T,T> forest,初始化forest[T]=T,然后修改forest[T]的内容就相当于修改Parent了。感兴趣可以自行百度

参考:百度百科

参考:字节笔试-老c和小m之间的放书矛盾

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值