第547题 省份数量(广度优先、并查集)

题目描述:

第547题 省份数量 (qq.com)https://mp.weixin.qq.com/s?__biz=Mzk0MjM5ODM2NQ==&mid=2247483736&idx=1&sn=10e2b925a97df321b9ed57256a1a5dbc&chksm=c2c28faaf5b506bc497b4f28c556d964dd78f3b3ea0386850cd82e7186961fdd47a7bd8321a1&token=1745957740&lang=zh_CN#rd


题目解析(广度优先):

对比我们上一篇文章所提到的深度优先算法,文字不好解释,直接用一个简单的图进行解释,如下图所示,倘若从市A开始搜索,按深度优先的原理我们应该先搜索B,然后根据B搜索到C,再去搜索到D,再根据D搜索到E,这样来遍历到与A直接相连和间接相连的所有城市,而广度优先则是先将与A直接相连的市全部搜索出来,在根据与A直接相连的市搜索到与A间接相连的市,最终得到这个省份。

class Solution {
    public int findCircleNum(int[][] isConnected) {
        int citys = isConnected.length;
        boolean[] visited = new boolean[citys];
        Queue<Integer> queue = new LinkedList<Integer>();
        int provinces = 0;
        for(int i = 0; i<citys ; i++){
            if(!visited[i]){
                queue.offer(i);
                while(!queue.isEmpty()){
                    int k = queue.poll();
                    visited[k] = true;
                    for(int j = 0; j <citys; j++){
                        if(!visited[j]){
                            if(isConnected[k][j]==1)queue.offer(j);
                        }
                    }
                }
                provinces++;
            }
        }
        return provinces;
    }
}

用了一个队列将与市直接相连的市入队,当队不为空时就一个个出队,并在出队的时候将该市标记为true,当对应的flag为flase时,表明该市还没有省份归属,我们就将与该市直接相连的市再入队,若已经为true时,则跳过即可,直到所有市遍历完。

执行用时:5 ms, 在所有 Java 提交中击败了16.10%的用户

内存消耗:41.7 MB, 在所有 Java 提交中击败了98.09%的用户


题目解析(并查集):

先介绍一下并查集的算法,我们可以将每个省份看做为一个多叉树,每个结点都指向其根结点,相当于什么呢,建立了一个线索指针,然后用一个数组head存储起来,再将每个多叉树的深度用另一个数组level存储起来,为什么这么做是为了之后合并时好操作,当两个多叉树之间存在相同的市的时候,说明他们其实应该属于同一个省份,我们就将这两个多叉树合并起来,那么怎么合并起来呢,只需要将一个多叉树的根节点连接到另一个树的根结点上就完成了合并,那么两棵树的情况下,谁应该连接谁呢,我们大概是这样处理的,如果两棵树的深度一样时,那么谁连谁都无所谓,当深度不一样时,我们将深度小的连接到深度大的即可,这样我们就不会增加树的深度。代码如下:
 

class Solution {
    int[] head;
    int[] level;
    public int findCircleNum(int[][] isConnected) {
        int citys = isConnected.length;
        head = new int[citys];
        level = new int[citys];
        int provinces = 0;
        for(int i = 0;i<citys;i++){
            head[i] = i;
            level[i] = 1;
        }
        for(int i = 0; i<citys-1;i++){
            for(int j = i+1;j<citys;j++){
                if(isConnected[i][j]==1)merge(i,j);
            }
        }
        for(int i = 0;i<citys;i++){
            if(head[i]==i)provinces++;
        }
        return provinces;
    }

    void merge(int x, int y){
        int i = find(x);
        int j = find(y);
        if(i==j)return;
        if(level[i]>level[j]){
            head[j] = i;
        }
        if(level[i]<level[j]){
            head[i] = j;
        }
        if(level[i]==level[j]){
            head[j] = i;
            level[i]++;
        }
    }

    int find(int pos){
        if(pos==head[pos]){
            return pos;
        }
        head[pos] = find(head[pos]);
        return head[pos];
    }
}

解释一下这个代码,首先我们用head数组来存储每个结点的根节点,用level数组来存储每个树对应的深度,首先进行初始化,因为在一开始每个结点都与自己相连,所以每个结点的根结点都是自己,深度都为1,遍历结点,当两个结点对应的值为1时,则合并两个结点。

merge函数,我们要合并两个结点,我们通过一个find函数找到两个结点对应的根节点,根结点相同的时候说明这两个市已经被划分到一个省份内,则返回即可,否则的话就根据之前提到的方法对两个结点进行合并即可,注意当两个树的深度相同时,合并时树的深度需要加1,不相同时,合并后树的深度不会增加。

find函数,我们通过一个递归函数找到该结点在多叉树中对应的根节点,只需要根据结点对应的根节点来进行寻找即可,比如存在这样一个数组[1,1,1,2,3],序号为这个市的序号,对应数组中的值为该市的根结点为谁,即该省的省份,通过市4找到市3,再通过市3找到市2,一直最后找到市1。也就是当该市的根节点为自己时,即找到了根结点。

最后要知道有几个省份,我们只需要遍历整个head数组,当有几个head[i]==i,则就有几个省份。

执行用时:1 ms, 在所有 Java 提交中击败了86.13%的用户

内存消耗:42.2 MB, 在所有 Java 提交中击败了65.79%的用户

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值