并查集quick union实现

并查集quick union实现

在这里插入图片描述

  • 将每个元素看成一个节点,每个节点都指向一个节点,当一个节点为根节点时其指向的节点为自身.
  • 连接节点7和3,即找到7的根节点5和3的根节点2,将根节点5指向2.即将节点7和节点2连接在了一起.
  • 在这种实现下,判断两个节点是否连接,只需要找到两个节点的根节点是否相同即可
  • 由于每个节点只会指向一个节点,根节点指向自身,所以可以用数组即可实现,数组记录每个节点指向的节点是什么.`
class uf2 :uf
{
public:
    uf2(int n)
    {
        sz = n;
        parent = new int[n];
        for (int i = 0;i < sz;i++)
        {
            parent[i] = i;
        }//初始时,每个元素都不连接,每个元素的父节点都是它本身
    }

    int getsize()//并查集考虑的元素的个数
    {
        return sz;
    }
    void unionelement(int p, int q)
    {
        assert(p >= 0 && p < sz);
        int proot = findroot(p);
        int qroot = findroot(q);
        if (proot == qroot)
            return;
        parent[proot] = qroot;
    }
    //查看元素p和元素q是否属于同一个集合
    bool  isconnented(int p, int q)
    {
        assert(p >= 0 && p < sz);
        return findroot(p) == findroot(q);
    }
private:
    int sz;
    int* parent;
    //查找元素的根节点
    //需要一直往上找时间复杂度为O(H)h为树的高度
    int findroot(int p)
    {
        //assert(p >= 0 && q < sz);
        //只要不是根节点一直向上找
        while (parent[p] != p)
        {
            p = parent[p];
        }
        return parent[p];
    }
   
};

基于size的优化

  • 当找到p和q的根节点时,合并时直接将p节点的根节点指向q的根节点,没有考虑p节点所在的树和q节点所在树的性质.这样新和成的树的高度可能不是最优,高度越高,下次查找的时候时间复杂度越高,效率越差.
    -
  • 假设合并 0,1 1,2 2,3最后树的退化成一条链表,查询时间复杂度高
  • 基于size的优化,每次让proot和qrootsize小的那个指向大的,这样有很大概率降低树的层数
  • 如上图,让3指向4,和让4指向3,让4指向3的树的高度低.
void unionelement(int p, int q)
    {
        assert(p >= 0 && p < sz);
        int proot = findroot(p);
        int qroot = findroot(q);
        if (proot == qroot)
            return;
        if (szz[proot] < szz[qroot])
            parent[proot] = qroot;
        else //包含了proot的size数量等于qroot的size数量的情况
            parent[qroot] = proot;
        parent[proot] = qroot;
    }

szz每个节点初始化为1;
- 基于size的优化其实本质是想优化树的高度,使得树的感度尽可能的小,这样查找根节点的时间复杂度O(h)也就小
但是在某些情况下,size的大小可能和树的高度没有本质的联系,所以使用树的高度rank优化更好

在这里插入图片描述
基于size的优化,会把8指向7,新生成的树的高度为4,但是此时吧7指向8,明显生成的树高度更小

基于rank的优化

合并的时候将rank小的根节点指向rank大根节点,那么rank大的节点的rank值还是不变.
只有当两个节点的rank值相等的时候,合并才需要更新rank值

void unionelement(int p, int q)
    {
        assert(p >= 0 && p < sz);
        int proot = findroot(p);
        int qroot = findroot(q);
        if (proot == qroot)
            return;
      
        if (rank[proot] < rank[qroot])
        {
            parent[proot] = qroot;
            //proot指向了qroot,qroot的rank比proot大,proot指向后,qootrank值不变
        }

        else if(rank[proot] > rank[qroot])
        { //同上
            parent[qroot] = proot;
        }
        else
        {
            parent[proot] = qroot;
            rank[qroot] += 1;
        }

    }

路径压缩

在这里插入图片描述
如上图所示,同样对4执行找根操作,由于树的高度不太,效率也不同
在向上找根节点的时候,当还未找到根节点的时候,不断执行parent[p]=parent[parent[p]];
例如上图找4,可以通过下面的操作将树变成右一的样子,树的高度降低

int findroot(int p)
    {
        //assert(p >= 0 && q < sz);
        //只要不是根节点一直向上找
        while (parent[p] != p)
        {
            p = parent[p];
            parent[p] = parent[parent[p]];
        }
        return parent[p];
    }

在路径压缩的过程中,树的高度一直在改变,但是没有更新rank的值.但是再合并的时候,仍可以通过rank的值来更新,只是rank数组此时不是记录的当前根所在树的高度,或者说是层数,这也是为什么拿rank命名而不拿height命名的原因

递归路径优化

在这里插入图片描述
利用递归进行路径压缩,让其经过路径压缩之后,该节点和到根节点路径上的所有节点全部直接指向根节点

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值