并查集

104 阅读2分钟

并查集结构

在一个集合中有N个元素,每一个元素都可以作为单独的一个集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

并查集具体的做法,查找两个元素是否属于同一个集合,如果两个不是同一个集合就涉及到两个元素的合并操作。

package course15;

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

/**
 * 并查集
 */
public class Code06_UnionFind {
    //节点
    public static class Node<V>{
         V value;
        public Node(V value) {
            this.value = value;
        }
    }

    public static class UnionSet<V>{
        /*包上一层*/
        public HashMap<V,Node<V>> nodes;
        /*找父亲的一个元素*/
        public HashMap<Node<V>,Node<V>> parents;
        /*这个元素集合下有多少个元素*/
        public HashMap<Node<V>,Integer>  sizeMap;


        /*初始化操作*/
        public UnionSet(List<V> value) {
            nodes = new HashMap<>();
            parents = new HashMap<>();
            sizeMap = new HashMap<>();

            for (V cur : value){
                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){
            Stack<Node<V>> path = new Stack<>();
            while(cur!=parents.get(cur)){
                path.push(cur);
                cur = parents.get(cur);
            }
            //这个操作是为了避免链式过长,这个是将他变的扁平化
            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){
            Node<V> aHead = findFather(nodes.get(a));
            Node<V> bHead = findFather(nodes.get(b));

            if (aHead != bHead){
                //查询 a,b集合的大小
                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);
                sizeMap.put(big,aSetSize+bSetSize);
                sizeMap.remove(small);
            }
        }
    }
}

练习题目

1、朋友圈

package course15;

public class Code08_FriendCircle {
    public static void main(String[] args) {
        int[][] nums = {{1,0,1,0,1},{0,1,0,1,0},{1,0,1,0,0},{0,1,0,1,0},{1,0,0,0,1}};
        int circleNum = findCircleNum(nums);
        System.out.println(circleNum);
    }


    public static int findCircleNum(int[][] M){
        int N = M.length;
        UnionFind unionFind = new UnionFind(N);
        for (int i = 0;i<M.length;i++){
            for (int j = i+1;j<M.length;j++){
                if (M[i][j] == 1){
                    unionFind.union(i,j);
                }
            }
        }
        return unionFind.getNumber();
    }


    public static class UnionFind{
        //parent[i] = k ,  i的父亲是k
        private int[] parent;
        //parent[i] = k ,  如果i是代表节点,size[i]才有意义,否则无意义
        //i所在的集合大小是多少,每一个集合下有多少的元素
        private int[] size;
        private int[] help;
        //一共有多少个集合
        private int sets;

        public UnionFind(int N) {
            parent = new int[N];
            size = new int[N];
            help = new int[N];
            //集合的数量
            sets = N;

            for (int i=0;i<N;i++){
                parent[i] = i;
                size[i] = 1;
            }
        }

        //向上找
        private int find(int i){
            int hi = 0;
            while(i != parent[i]){
                help[hi++] = i;
                i = parent[i];
            }
            for (hi--;hi>=0;hi--){
                parent[help[hi]] = i;
            }
            return i;
        }

        //合并
        public void union(int i,int j){
            int f1 = find(i);
            int f2 = find(j);
            if (f1!=f2){
                if (size[f1] >= size[f2]){
                    size[f1] += size[f2];
                    parent[f2] = f1;
                }else{
                    size[f2] += size[f1];
                    parent[f1] = f2;
                }
                sets--;
            }
        }
        public int getNumber(){
            return sets;
        }
    }
}

2、岛屿问题

package course15;

public class Code09_LandsNum {
    public static void main(String[] args) {
        char[][] nums = {
                {'1','0','0','0','1','1','0'},
                {'1','1','1','0','0','1','0'},
                {'0','0','0','0','1','1','0'},
                {'0','1','0','0','0','1','0'},
                {'1','1','0','0','0','0','0'},
                {'1','0','0','0','1','1','0'},};

        System.out.println(islandNum3(nums));
    }

    public static int islandNum3(char[][] board){
        int islands = 0;
        int N = board.length;
        for (int i=0;i<N;i++){
            for (int j=0;j<board[0].length;j++){
                if (board[i][j] == '1'){
                    islands++;
                    infect(board,i,j);
                }
            }
        }
        return islands;
    }

    private static void infect(char[][] board, int i, int j) {
        if (i<0 || i == board.length || j<0 || j == board[0].length || board[i][j]!='1'){
            return;
        }
        board[i][j] = '2';
        infect(board,i-1,j);
        infect(board,i+1,j);
        infect(board,i,j+1);
        infect(board,i,j-1);

    }
}

3、相同的用户

package course15;

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

public class Code07_isSamePersons {
    private static class User{
        String idCard;
        String bid;
        String gitHubId;
        public User() {}
        public User(String idCard, String bid, String gitHubId) {
            this.idCard = idCard;
            this.bid = bid;
            this.gitHubId = gitHubId;
        }
    }

    public static int mergeUsers(List<User> users){
        UnionSet unionSet = new UnionSet(users);
        HashMap<String,User> mapIdCard = new HashMap<>();
        HashMap<String,User> mapBid = new HashMap<>();
        HashMap<String,User> mapGitHubId = new HashMap<>();


        for (User user : users){
            if (mapIdCard.containsKey(user.idCard)){
                unionSet.union(user.idCard,mapIdCard.get(user.idCard));
            }else{
                mapIdCard.put(user.idCard,user);
            }

            if (mapBid.containsKey(user.bid)){
                unionSet.union(user.bid,mapIdCard.get(user.bid));
            }else{
                mapIdCard.put(user.bid,user);
            }

            if (mapGitHubId.containsKey(user.gitHubId)){
                unionSet.union(user.gitHubId,mapIdCard.get(user.gitHubId));
            }else{
                mapIdCard.put(user.gitHubId,user);
            }
        }
        return unionSet.getSetNum();
    }
    
    public static class Node<V>{
        V value;
        public Node(V value) {
            this.value = value;
        }
    }

    public static class UnionSet<V>{
        /*包上一层*/
        public HashMap<V, Node<V>> nodes;
        /*找父亲的一个元素*/
        public HashMap<Node<V>, Node<V>> parents;
        public HashMap<Node<V>,Integer>  sizeMap;

        public UnionSet(List<V> value) {
            nodes = new HashMap<>();
            parents = new HashMap<>();
            sizeMap = new HashMap<>();
            for (V cur : value){
                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){
            Stack<Node<V>> path = new Stack<>();
            while(cur!=parents.get(cur)){
                path.push(cur);
                cur = parents.get(cur);
            }
            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){
            Node<V> aHead = findFather(nodes.get(a));
            Node<V> bHead = findFather(nodes.get(b));

            if (aHead != bHead){
                //查询 a,b集合的大小
                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);
                sizeMap.put(big,aSetSize+bSetSize);
                sizeMap.remove(small);
            }
        }
        public int getSetNum() {
            return sizeMap.size();
        }
    }
}