并查集

324 阅读2分钟

案例:

首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路……

上述问题转化为并查集,即只需要计算有多少个集合。 并查集详解,参考:blog.csdn.net/liujian2015…

解决相关问题:等式方程的可满足性

并查集C++实现:

class unionFindSet {
    vector<int> ufs;
public:
    unionFindSet(int size) {
        ufs.resize(size, -1); 
        // 初始化为-1,表示每个节点一个集合,每个集合的元素总数为1(-1的绝对值)
    }

    // 合并两个集合
    void unionSet(int idx1, int idx2) {
        // 首先找到两个集合各自的根节点
        int root1 = findRoot(idx1);
        int root2 = findRoot(idx2);
        // 根节点相同,证明原本就属于一个集合,无需合并
        if (root1 == root2) {
            return;
        }
        // 若idx非集合根节点,则ufs[idx]表示其上级节点的索引(正值)
        // 若idx为某一集合根节点,则ufs[idx]的绝对值表示该集合元素总数
        // 合并两个集合,将两集合元素总数相加
        // 合并后,root1为新集合的根节点,root2的上级节点为root1
        ufs[root1] += ufs[root2];
        ufs[root2] = root1;
    }   

    // 查找idx所在集合的根节点
    void findRoot(int idx) {
        // ufs[idx] < 0, idx为根节点
        // ufs[idx] > 0时,ufs[idx]表示当前节点的上级节点的索引,继续向上查找
        while (ufs[idx] > 0) {
            idx = ufs[idx];
        }
        return idx;
    }

    // 存在多少个集合,即ufs[idx] < 0 的值总数
    int count() {
        int count = 0;
        for (int i = 0; i < ufs.size(); ++i) {
            if (ufs[i] < 0) {
                ++count;
            }
        }
        return count;
    }
}