并查集基础学习

在这里插入图片描述

在这里插入图片描述
图例简单描述一下:
在这里插入图片描述
代码描述:

package data.structure.unionfind;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class UnionSet<V> {

    class Node<V> {
        private V val;
        public Node(V val) {
            this.val = val;
        }
    }


    private Map<V, Node<V>> nodeMap;
    private Map<Node<V>, Node<V>> parents;
    private Map<Node<V>, Integer> rootSizeMap;


    public int getConnectedSize() {
        return rootSizeMap.size();
    }

    public UnionSet(List<V> vals) {
        nodeMap = new HashMap<>();
        parents = new HashMap<>();
        rootSizeMap = new HashMap<>();
        for (V val : vals) {
            Node<V> cur = new Node<>(val);
            nodeMap.put(val, cur);
            parents.put(cur,cur);// 开始自己指向自己
            rootSizeMap.put(cur, 1);
        }
    }

    public boolean unionFind(V val1, V val2) {

        if (!nodeMap.containsKey(val1) || !nodeMap.containsKey(val2)) {
            return false;
        }

        return findFather(val1) == findFather(val2);

    }

    public void union(V val1, V val2) {

        Node<V> fatherOfVal1 = findFather(val1);
        Node<V> fatherOfVal2 = findFather(val2);
        if (fatherOfVal1 == fatherOfVal2) {
            return;
        }
        Integer fatherOfVal1Size = rootSizeMap.get(fatherOfVal1);
        Integer fatherOfVal2Size = rootSizeMap.get(fatherOfVal2);

        // 大的节点
        Node<V> bigFather = fatherOfVal1Size >= fatherOfVal2Size ? fatherOfVal1 : fatherOfVal2;
        // 小的节点
        Node<V> smallFather = bigFather == fatherOfVal1 ? fatherOfVal2 : fatherOfVal1;
        // 小的节点指向大的节点
        parents.put(smallFather, bigFather);
        // 更新大的节点作为根节点维护的子元素的数量
        rootSizeMap.put(bigFather, rootSizeMap.get(bigFather) + rootSizeMap.get(smallFather));
        // 小的节点被吞并了,可以从Map里面剔除掉了
        rootSizeMap.remove(smallFather);
    }

    private Node<V> findFather(V val) {

        Stack<Node<V>> cacheNodeInList = new Stack<>();
        Node<V> cur = nodeMap.get(val);
        cacheNodeInList.push(nodeMap.get(val));
        while (parents.get(cur) != null && cur != parents.get(cur)) {
            Node<V> temp = parents.get(cur);
            cacheNodeInList.push(temp);
            cur = temp;
        }
        // 找到了根节点, 这里做一个优化,就是将整个链表上的节点展平, 都一度指向根节点(原先可能是个链)
        while (!cacheNodeInList.isEmpty()) {
            parents.put(cacheNodeInList.pop(), cur);
        }

        return cur;

    }

}

同时可以了解一下并查集Leetcode上面的一道习题集:
在这里插入图片描述
用上述并查集的思想解决如下:

package leetcode.editor.cn;

import java.util.*;
import java.util.stream.Collectors;

public class P721AccountsMerge {
    public static void main(String[] args) {
        Solution solution = new P721AccountsMerge().new Solution();
        // [["David","David0@m.co","David4@m.co","David3@m.co"],["David","David5@m.co","David5@m.co","David0@m.co"],["David","David1@m.co","David4@m.co","David0@m.co"],
        // ["David","David0@m.co","David1@m.co","David3@m.co"],["David","David4@m.co","David1@m.co","David3@m.co"]]
        List<List<String>> data = new ArrayList<>();
        List<String> t1 = new ArrayList<>();
        t1.add("David");
        t1.add("David0@m.co");
        t1.add("David4@m.co");
        t1.add("David3@m.co");
        data.add(t1);
        List<String> t2 = new ArrayList<>();
        t2.add("David");
        t2.add("David0@m.co");
        t2.add("David4@m.co");
        t2.add("David3@m.co");
        data.add(t2);
        /*List<String> t2 = new ArrayList<>();
        t2.add("David");
        t2.add("David5@m.co");
        t2.add("David5@m.co");
        t2.add("David0@m.co");
        data.add(t2);

        List<String> t3 = new ArrayList<>();
        t3.add("David");
        t3.add("David1@m.co");
        t3.add("David4@m.co");
        t3.add("David0@m.co");
        data.add(t3);

        List<String> t4 = new ArrayList<>();
        t4.add("David");
        t4.add("David0@m.co");
        t4.add("David1@m.co");
        t4.add("David3@m.co");
        data.add(t4);

        List<String> t5 = new ArrayList<>();
        t5.add("David");
        t5.add("David4@m.co");
        t5.add("David1@m.co");
        t5.add("David3@m.co");
        data.add(t5);*/

        List<List<String>> lists = solution.accountsMerge(data);



        System.out.println("Hello world" + lists);
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {


        public class ValNode<V> {
            private V val;
            public ValNode(V val) {
                this.val = val;
            }
        }
        private Map<String, List<Integer>> emailMap = new HashMap<>();
        private Map<List<String>, ValNode<List<String>>> nodeMap = new HashMap<>();
        private Map<ValNode<List<String>>, ValNode<List<String>>> parents= new HashMap<>();

        private Map<ValNode<List<String>>, Set<String>> rootSizeMap= new HashMap<>();
        public List<List<String>> accountsMerge(List<List<String>> accounts) {

            int accountindex = 0;
            for (List<String> account : accounts) {
                ValNode<List<String>> valNode = new ValNode<List<String>>(account);
                nodeMap.put(account, valNode);
                parents.put(valNode, valNode);
                for (int i = 1; i < account.size();i++) {
                    if (emailMap.containsKey(account.get(i))) {
                        List<Integer> integers = emailMap.get(account.get(i));
                        integers.add(accountindex);
                        emailMap.put(account.get(i), integers);
                    } else {
                        List<Integer> init = new ArrayList<>();
                        init.add(accountindex);
                        emailMap.put(account.get(i), init);
                    }
                }
                accountindex++;
            }
            nodeMap.entrySet().stream().forEach(entry -> {
                rootSizeMap.put(entry.getValue(), new HashSet<>(entry.getKey().subList(1, entry.getKey().size())));
            });

            for (Map.Entry<String, List<Integer>> entry : emailMap.entrySet()) {
                List<Integer> emailEqualList = entry.getValue();
                for (int i = 0; i < emailEqualList.size() - 1; i++) {
                    for (int j = i + 1; j < emailEqualList.size(); j++) {
                        ValNode<List<String>> n1 = findFather(accounts.get(emailEqualList.get(i)));
                        ValNode<List<String>> n2 = findFather(accounts.get(emailEqualList.get(j)));
                        if (n1 != n2)  {
                            mergerUnion(n1, n2);
                        }
                    }
                }
            }
            // handle result
            List<List<String>> list = new ArrayList<>();
            for (Map.Entry<ValNode<List<String>>, Set<String>> entry : rootSizeMap.entrySet()) {
                List<String> result = new ArrayList<>();
                String name = entry.getKey().val.get(0);
                result.add(name);
                result.addAll(entry.getValue().stream().sorted().collect(Collectors.toList()));
                list.add(result);
            }
            return list;


        }

        private void mergerUnion(ValNode<List<String>> n1, ValNode<List<String>> n2) {
            Set<String> result1 = rootSizeMap.get(n1);
            Set<String> result2 = rootSizeMap.get(n2);
            ValNode<List<String>> big = result1.size() >= result2.size() ? n1 : n2;
            ValNode<List<String>> small = big == n1 ? n2 : n1;
            parents.put(small, big);
            result1.addAll(result2);
            rootSizeMap.put(big, result1);
            rootSizeMap.remove(small);
        }

        public ValNode<List<String>> findFather(List<String> l1) {
            ValNode<List<String>> n1 = nodeMap.get(l1);
            Stack<ValNode<List<String>>> stack = new Stack<>();
            stack.push(n1);
            ValNode<List<String>> cur = n1;
            while (parents.get(cur) != null && cur != parents.get(cur)) {
                ValNode<List<String>> temp = parents.get(cur);
                stack.push(temp);
                cur = temp;
            }
            while (!stack.isEmpty()) {
                parents.put(stack.pop(), cur);
            }
            return cur;

        }
}
//leetcode submit region end(Prohibit modification and deletion)

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值