认识并查集结构

并查集是1964年别人脑补的一个算法,到证明结束是1989年,这个证明也是够漫长的。

并查集的效率非常高,当有N个数据的时候,假设查询次数到了N之后,其时间复杂度仅为O(1)!!

并查集使用哈希是来做的,所以节点中并不需要放些什么。FatherMap中key 是当前节点,value是当前节点的父节点。然后是sizeMap,key是当前节点,value是点当前节点的所在集合的节点总个数。

getHeadNode:获取当前节点的头节点,为了提高之后的效率,每次获取都将不与头节点相连的节点改为与头节点直接相连(也就是所谓的路径压缩了),整个过程由递归来解决,可以节省很多代码,当然,也可以自己压栈

union:将两个节点所在的集合 合并。将节点数少的哪一个集合 合并到节点数多的那个集合的头节点,然后除了节点数多的那个头节点需要更换size之外,别的节点不需要更改,因为以后再也不会使用到了。


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

public class C03_UnionFind {
    public static class Node{
        //写什么都行
    }
    public static class unionFind{
        private HashMap<Node, Node>fatherMap;
        private HashMap<Node, Integer>sizeMap;
        public unionFind(){//构造
            fatherMap = new HashMap<Node, Node>();
            sizeMap = new HashMap<Node, Integer>();
        }
        //获取头节点(递归版)
        public Node getHeadNode(Node cur){
            Node fatherNode = fatherMap.get(cur);
            if(cur!=fatherNode){
                cur = fatherNode;
                fatherNode = fatherMap.get(cur);
                getHeadNode(cur);
            }
            return fatherNode;
        }
        //获取头节点(非递归版)
        public Node getHeadNode2(Node node){
            Stack<Node>stack = new Stack<Node>();
            Node cur = node;
            Node fatherNode = fatherMap.get(cur);
            //将除头节点以外的节点压进栈里
            while(fatherNode != cur){
                stack.push(cur);
                cur = fatherNode;
                fatherNode = fatherMap.get(cur);
            }
            //到这里fatherNode就是头节点了
            //出栈,并且将节点都指向头节点
            while(!stack.isEmpty()){
                fatherMap.put(stack.pop(), fatherNode);
            }
            return fatherNode;
        }
        //合并两个节点所在的集
        public void union(Node n1,Node n2){
            if(n1==null || n2==null){
                return ;
            }
            Node head1 = getHeadNode(n1);
            Node head2 = getHeadNode(n2);
            if(head1 == head2){
                return ;
            }
            if(sizeMap.get(head1)>sizeMap.get(head2)){
                //head1的个数多就将head2接到head1上
                //只需要改变head1的size,因为他的子节点已经不需要用到size了
                fatherMap.put(head2, head1);
                sizeMap.put(head1, sizeMap.get(head1)+sizeMap.get(head1));
            }else {
                fatherMap.put(head1, head1);
                sizeMap.put(head2, sizeMap.get(head1)+sizeMap.get(head2));
            }
        }
        //两个节点是否来自同一个集合,只看他们的头是否一样
        public boolean isSameSet(Node n1,Node n2){
            return getHeadNode(n1)==getHeadNode(n2);
        }
        //初始化
        public void init(List<Node>nodes){
            fatherMap.clear();
            sizeMap.clear();
            for (Node node : nodes) {
                sizeMap.put(node, 1);
                fatherMap.put(node, node);
            }
        }
    }
    public static void main(String[] args) {
        
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值