12-并查集

12. 并查集

12.1 并查集

1. 题目

并查集提供两个功能:

1. 看两个元素是否是同一个集合
1. 将两个元素所在集合的全体合
1. 均摊下来(比如一百万的数据,有一亿查询)是O(1)

2. 思路

​ 判断集合:每个节点中加入一个指针,初始都指向自己。这个指针一直往上找,找到最上面的就是一个集合的代表节点,判断两个节点的代表结点是否一样,就可以知道这两个节点是不是同一个集合里的了。

​ 合并:e的代表结点指向a,那就代表二者合并。如果有个c想加入,a长度为3,c长度为5,小的挂载大的上。

​ 关键:找到代表节点(当前节点一直向上的根)

3. 代码

节点:

public static class Node<V> {
    V value;
    public Node(V v) {
        value = v;
    }
}

所需的结构:

// 将用户给的节点包装成自己的Node
public HashMap<V, Node<V>> nodes;
// 记录当前节点的父节点所在,Node中加指针也行无所谓
public HashMap<Node<V>, Node<V>> parents;
// 记录代表节点的size,只有代表结点在里面存,
// 合并后不再是代表结点的节点会被移除表
public HashMap<Node<V>, Integer> sizeMap;

初始化:

public UnionFind(List<V> values) {
    nodes = new HashMap<>();
    parents = new HashMap<>();
    sizeMap = new HashMap<>();
    // 节点初始化,每个节点都指向自己(parents<自己,自己>)
    for (V cur : values) {
        Node<V> node = new Node<>(cur);
        nodes.put(cur, node);
        parents.put(node, node);
        sizeMap.put(node, 1);
    }
}

代表节点:

// 找到代表节点
public Node<V> findFather(Node<V> cur) {
    // 优化:为了让我们的并查集扁平化
    // 当查询的时候,将路径上的所有值都保存下来
    // 找到代表节点的时候,就把这些值的parents都指向代表结点
    // 这样就保证查询O(1)
    Stack<Node<V>> path = new Stack<>();
    // 路径压栈,直到找到一个父节点为自己的节点(代表结点)
    while (cur != parents.get(cur)) {
        path.push(cur);
        cur = parents.get(cur);
    }
    // 走到头之后,cur就是代表节点
    // 这里的循环开始是put(代表节点,代表节点)无伤大雅
    while (!path.isEmpty()) {
        parents.put(path.pop(), cur);
    }
    return cur;
}

判断是否为同一个节点:

// 判断两个节点是否为同一个代表节点即可
public boolean isSameSet(V a, V b) {
    return findFather(nodes.get(a)) == findFather(nodes.get(b));
}

合并节点:

// 合并节点
public void union(V a, V b) {
    // 1. 找到代表节点
    Node<V> aHead = findFather(nodes.get(a));
    Node<V> bHead = findFather(nodes.get(b));
    if (aHead != bHead) {
        // 获取size
        int aSetSize = sizeMap.get(aHead);
        int bSetSize = sizeMap.get(bHead);
        // 确定谁大谁小
        Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
        Node<V> small = big == aHead ? bHead : aHead;
        // 小的加入到大的上
        parents.put(small, big);
        // 更新size,并且删除小的size
        sizeMap.put(big, aSetSize + bSetSize);
        sizeMap.remove(small);
    }
}
posted @ 2023-11-09 09:04  犹豫且败北  阅读(59)  评论(0)    收藏  举报