并查集
大约 1 分钟
并查集
并查集的功能
- 合并(Union): 合并两个集合,
- 查询(Find): 查询某个元素是否在集合中.
核心要点
- 以树的形式维护集合.
- 集合的编号为根节点的编号. 根节点为代表元素.
- 每个节点都保存其父节点的编号, 用于查找元素属于的集合.
用什么数据结构存储?
使用数组进行维护.
// 并查集
int[] p;
// 并查集大小(可选)
int[] size;
如何判断树根?
public boolean isRoot(int x) {
    return p[x] == x;
}
如何求元素对应的集合编号?
// 这里进行了路径压缩: 通过某个路径找到根节点, 则将该路径上的所有节点都指向根节点.
public int find(int x) {
    if (p[x] != x) {
        p[x] = find(p[x]);
    }
    return p[x];
}
如何合并集合?
// 将 x 所在的集合和 y 所在的集合合并
public void merge(int x, int y) {
    p[find(x)] = find(y);
    // 或者 p[find(y)] = find(x);
}
例题

public class acwing_838 {
    int[] p;
    int n, m;
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    public void mergeCollections() throws IOException {
        String nums = reader.readLine();
        String[] numsSplit = nums.split(" ");
        n = Integer.parseInt(numsSplit[0]);
        m = Integer.parseInt(numsSplit[1]);
        p = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            p[i] = i;
        }
        List<String> res = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            String op = reader.readLine();
            String[] ops = op.split(" ");
            if (ops[0].equals("M")) {
                merge(Integer.parseInt(ops[1]), Integer.parseInt(ops[2]));
            } else if (ops[0].equals("Q")) {
                res.add(find(Integer.parseInt(ops[1])) == find(Integer.parseInt(ops[2])) ? "Yes" : "No");
            }
        }
        res.forEach(System.out::println);
    }
    int find(int x) {
        if (p[x] != x) {
            p[x] = find(p[x]);
        }
        return p[x];
    }
    void merge(int x, int y) {
        p[find(x)] = find(y);
    }
    public static void main(String[] args) {
        acwing_838 acwing838 = new acwing_838();
        try {
            acwing838.mergeCollections();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
