P37-省份数量-并查集

//省份数量
/*
* 有n个城市,其中一些彼此相连,另一些没有相连。如果城市a与城市b直接相连,且城市b与城市c直接相连,
* 那么城市a与城市c间接相连
* 省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
* 给你一个 n * n 的矩阵 isConnected,其中isConnected[i][j]=1表示第i个城市和第j个城市直接相连,
* 而isConnected[i][j]=0表示二者不直接相连
* 返回矩阵中省份(直接或间接相连的是一个省,没有关联的是另一个省)的数量
* */
public class P37 {
    public static void main(String[] args) {
        System.out.println(getProvince(new int[][]{{1,1,0},{1,1,0},{0,0,1}}));  //2
        System.out.println(getProvince(new int[][]{{1,0,0},{0,1,0},{0,0,1}}));  //3
    }

    //并查集,并是合并两个树,查是查根节点,直到该合并的都合并了,树和树之间再没办法合并,题目就解开了
    //合并的时候,是以树高度大的作为根节点,可以减少维护所有节点指向新节点的次数,并且合并后高度还是不变
    //思路:一个一维数组代表城市,不同下标就是不同城市,数组的值是root节点的下标,最后统计这个数组有多少不同的root,就是多少个省
    //直接统计不太好,还可以是 数组下标 == 数组值,就是一个省

    //并查集
    private static int getProvince(int[][] citiesConnected) {
        int cities = citiesConnected.length;     //初始假设每一个城市都是一个省
        int[] head = new int[cities];
        int[] level = new int[cities];       //维护层高,用来减少维护head数组的。
        // 合并的时候,层高相同时,值都加1
        // 层高不同时,小的变为大的值

        for (int i=0; i<cities; i++){
            head[i] = i;        //初始化
            level[i] = 1;
        }

        for(int i=0; i<cities; i++){
            for(int j=i+1; j<cities; j++){
                if(citiesConnected[i][j] == 1){     //属于同一个省,合并
                    merge(i, j, head, level);
                }
            }
        }

        //统计省份数量
        int provinces = 0;
        for(int i=0; i<cities; i++){
            if(head[i] == i){
                provinces++;
            }
        }

        return provinces;
    }

    //合并方法
    static void merge(int x, int y, int[] head, int[] level){
        int i = find(x, head);        //找x的根节点
        int j = find(y, head);        //找y的根节点

        if(i == j){     //代表同一个树
            return;
        }

        //不是同一个树
        if(level[i] <= level[j]){          //合并,矮的合到高的上
            head[i] = j;        //i的level要++
        }else{
            head[j] = i;        //j的level要++
        }

        if(level[i] == level[j]){
            level[i]++;     //比较准确的是把这个判断放在上面,看是i还是j++,不过其实都++并不影响结果,不影响level[i] <= level[j]判断结果
            level[j]++;
        }
    }

    //找根节点
    //如【0,1,1,2,3】,这样root共有2个,分别是0和1索引,但假如4号索引要找根节点,需要递归,直到找到值等于下标的值
    private static int find(int x, int[] head) {
        if(head[x] == x){
            return x;
        }
        head[x] = find(head[x], head);     //还要维护指针,这样下次就不用再次递归找root(head)了
        //[0,1,1,2,1]
        return head[x];
    }

}

 

posted @ 2022-04-11 11:43  YonchanLew  阅读(43)  评论(0)    收藏  举报