并查集

并查集

https://www.luogu.com.cn/problem/P3367

并查集问题非常经典,解法非常精妙

首先对每个元素做标号

数据结构就是一个数组,数组中存储的是对应根节点的标号

//初始化
for (int i=1;i<=k;i++) {
    f[i]=i;
}

所谓并查集,也就只有两个操作,并和查

首先是查操作

int find(int k) {
    //如果根节点是自己,那么返回
    if(f[k]==k)return k;
    //否则找父节点的根节点,并且做一下路径压缩,将父节点的根节点置为自己的根节点
    return f[k]=find(f[k]);
}

然后是并操作

void merge(int a, int b) {
    //把a的根节点挂到b的根节点上,如果只把a挂到b上,就无法顾及到a的根节点上的其他节点了
    f[find(a)]=find(b);
}

非常巧妙,短短几行代码,实现了如此复杂而又高效的逻辑

带权并查集

无权的并查集应用范围还是比较有限,加上权重,就能实现更复杂的逻辑,例如leetcode 399. 除法求值

带上权重的话,就需要添加一个数组,存储权重值,初始化为1

在这道题中,权重是商,那么 w[i] 含义就是 N[i] = w[i] * N[f[i]]

for (int i=1;i<=k;i++) {
    f[i]=i;
    w[i]=1;
}

查操作和并操作

int find(int x) {
    if (f[x]==x) return x;
    int y=f[x];
    int z=find(y);
    //每进行一次find操作,w数组也被更新过了,因此用更新过的y再更新x
    w[x]=w[x]*w[y];
    return f[x]=z;
}

void merge(int a, int b, double k) {
    int aa=find(a);
    int bb=find(b);
    f[aa]=bb;
    // a/b=k  a=w[a]*aa  b=w[b]*bb  w[aa]=aa/bb 推导可得 
    w[aa]=k*w[b]/w[a];
}

posted @ 2021-01-20 15:17  十月sy  阅读(52)  评论(0)    收藏  举报