并查集习题整理

并查集模板

基本原理:用一棵树来表示一个集合,树根节点是集合编号。每个节点存储用 来存储父节点,例如 f [a] 表示a的父节点。这样可以在O(1)时间内合并两个集合,O(1)时间内询问两个元素是否在一个集合中。
	//需要初始化为-1
 	int p[N];

    int find(int x){
     
    	//p[x]若为负数则是根节点,负数的绝对值代表集合元素的个数
        if(p[x] <= -1) return x;
        //不是负数则继续向上寻找并压缩路径
        else return p[x] = find(p[x]);
    }
    //维护到顶点距离的并查集
	int find(int x){
     
	    if(p[x] <= -1) return x;
	    else{
     
	        //保存下x的父节点
	        int t = p[x];
	        //父节点更新为根节点
	        p[x] = find(p[x]);
	        d[x] += d[t];
	        return p[x];
	    }
	}

    void merge(int x, int y){
     
        int px = find(x), py = find(y);
        if(px == py) return ;
        else{
     
        	//小集合归并到大集合
            if(p[px] <= p[py]) p[px] += p[py], p[py] = px;
            else p[py] += p[px], p[px] = py;
        }
    }

LeetCode684. 冗余连接

class Solution {
     
public:
    vector<int> p;

    int find(int x){
     
        if(p[x] <= -1) return x;
        else return p[x] = find(p[x]);
    }

    bool merge(int x, int y){
     
        int px = find(x), py = find(y);
        if(px == py) return false;
        else{
     
            if(p[px] <= p[py]) p[px] += p[py], p[py] = px;
            else p[py] += p[px], p[px] = py;
            return true;
        }
    }
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
     
        int n = edges.size();
        p.resize(n+1, -1);
        for(auto& t: edges){
     
            if(!merge(t[0], t[1])) return t;
        }
        return {
     -1, -1};
    }
};

acwing240. 食物链

维护到根节点距离的并查集的典型应用,只要两个元素在一个集合里面,通过它们与根节点的距离就能知道它们的相对关系。通过到根节点距离 % 3的余数将元素分成三类,余数为0,则与根节点属于同一类,余数为1可以吃掉根节点这一类,余数为2可以被根节点这一类吃。

#include 
using namespace std;

const int N = 50005;
int p[N], d[N], n, k;

//维护距离的并查集
int find(int x){
     
    if(p[x] <= -1) return x;
    else{
     
        //保存下x的父节点
        int t = p[x];
        //父节点更新为根节点
        p[x] = find(p[x]);
        d[x] += d[t];
        return p[x];
    }
}


int main(){
     
    memset(p, -1, sizeof p);
    scanf("%d %d", &n, &k);
    int op, x, y, ans = 0;
    while(k --){
     
        scanf("%d %d %d", &op, &x, &y);
        if(x > n || y > n) ans ++;
        else{
     
            int px = find(x), py = find(y);
            if(op == 1){
     
            	//若在同一个集合中,且两者距离之差模3为0,则属于同一类
                if(px == py && (d[x] - d[y]) % 3) ans ++;
                //若两者不在同一个集合,则将x所在集合加入y所在集合中
                else if(px != py){
     
                	//x根节点指向y根节点
                    p[px] = py;
                    //为保证两者之间距离只差模3为0,可把d[x]设为和d[y]同样大小
                    d[px] = d[y] - d[x];
                }
            }
            else{
     
            	//若在同一个集合中,且两者距离之差模3为1,则x可吃y
                if(px == py && (d[x] - d[y]-1) % 3) ans ++;
                else if(px != py){
     
                    p[px] = py;
                    //为保证两者之间距离只差模3为0,可把d[x]设为和d[y]+1
                    d[px] = d[y] - d[x] + 1;
                }
            }
        }
    }
    printf("%d", ans);
    return 0;
}

你可能感兴趣的:(leetcode,算法,数据结构)