并查集

主要操作:
合并:unionElements(p,q);
查找:find(p);

class UnionFind{

private:
    int* id;
    int count;
public:
    UnionFind(int n){
        count=0;
        id=new int[n];
        for(int i=0;i<n;i++)
            id[i]=i;
    }
    ~UnionFind(){
        delete [] id;
    }

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

    bool isConnected(int p,int q){
        return find(p)==find(q);
    }
    //存在很大优化空间
    void unionElements(int p,int q){
        int pID=find(p);
        int qID=find(q);
        if(pID==qID)
            return;     
        for(int i=0;i<count;i++){
            if(id[i]==pID)
                id[i]=qID;
        }
    }
};

改进版本:
将每个元素看作是一个节点,parent(i)为它的根节点,进行连接操作时,直接把元素指向根节点即可。

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;
    }
    //找p元素的根节点
    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;
    }
};


上面标记的优化之处在于:当连接两个元素时,有可能将大集合中的根节点指向元素少的集合的根节点,这样树的高度就会大幅增加,给查找带来了不便。因此,可以事先比较集合的元素个数,保证每次都能把小集合中的元素指向元素多的集合的根节点。
优化如下:

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;    //初始每个集合大小都为1
        } 
    }
    ~unionFind(){
        delete [] parent;
        delete [] sz;
    }

    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];//集合大小更新
        }           
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值