并查集

  1. 根节点的定义

    • 在并查集中,每个集合由一棵树表示,树的根节点是该集合的代表元素。
    • 对于根节点,它的父节点指向自己,即 parent[root] == root
  2. 查找根节点

    • 当调用 find(x) 时,我们需要找到节点 x 所属集合的根节点。
    • 如果 parent[x] == x,说明 x 就是根节点,直接返回 x
    • 如果 parent[x] != x,说明 x 不是根节点,需要继续向上查找其父节点的根节点。
  3. 路径压缩

    • 在查找过程中,通过递归调用 find(parent[x]),我们可以一直向上查找直到找到根节点。
    • 同时,路径压缩技术会在递归返回的过程中,将路径上的所有节点直接指向根节点,从而减少未来的查找时间。
  4. 合并操作 (unionSets(int x, int y)):

    • 查找两个元素所属的集合的根节点。
    • 如果两个根节点不同,比较它们的秩:
      • 如果 rank[rootX] > rank[rootY],则将 rootY 连接到 rootX
      • 如果 rank[rootX] < rank[rootY],则将 rootX 连接到 rootY
      • 如果 rank[rootX] == rank[rootY],则任意选择一个作为新的根节点,并将新根节点的秩加1。

通过这种方式,rank 帮助保持并查集的树结构尽可能平衡,从而提高查找和合并操作的效率。


#include <iostream>
#include <vector>

class UnionFind {
private:
    std::vector<int> parent;
    std::vector<int> rank;  // 用于记录每个集合的“秩”(也可以理解为树的高度的上界)

public:
    // 构造函数,初始化并查集
    UnionFind(int n) {
        parent.resize(n);
        rank.resize(n, 0);  // rank 数组初始化为0,表示每个节点初始时都是一棵树,高度为1。
        for (int i = 0; i < n; ++i) {
            parent[i] = i;  // 每个元素都是自己的父节点,树根的父节点指向自己
        }
    }

    // 查找操作,路径压缩
    int find(int x) {
        if (parent[x] != x) {
            // 使用路径压缩技术,递归地查找元素x的根节点,并将路径上的所有节点直接指向根节点
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    // 合并操作,按秩合并
    void unionSets(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        /*
            查找两个元素所属的集合的根节点。
            如果两个根节点不同,比较它们的秩:
                如果 rank[rootX] > rank[rootY],则将 rootY 连接到 rootX。
                如果 rank[rootX] < rank[rootY],则将 rootX 连接到 rootY。
                如果 rank[rootX] == rank[rootY],则任意选择一个作为新的根节点,并将新根节点的秩加1。
        */
        if (rootX != rootY) {
            if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            } else if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            } else {
                parent[rootY] = rootX;
                rank[rootX] += 1;  // 两个树的秩相同,新根节点的秩加1
            }
        }
    }

    // 判断两个元素是否属于同一个集合
    bool connected(int x, int y) {
        return find(x) == find(y);
    }
};

// 示例使用
int main() {
    int n = 10;  // 创建一个包含10个元素的并查集
    UnionFind uf(n);

    // 进行合并操作
    uf.unionSets(1, 2);
    uf.unionSets(2, 3);

    // 检查元素1和3是否在同一个集合中
    std::cout << "Elements 1 and 3 are in the same set: " 
              << (uf.connected(1, 3) ? "True" : "False") << std::endl;

    return 0;
}
posted @ 2024-10-15 09:02  guanyubo  阅读(5)  评论(0编辑  收藏  举报