Leetcode0547. 省份数量(medium,DFS,BFS,并查集)

目录

1. 题目描述

2. 解题分析

3. 代码实现


目录

1. 题目描述

2. 方法一:图遍历

2.1 思路

2.2 代码实现

3. 方法二:并查集

3.1 思路

3.2 代码实现


1. 题目描述

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2

示例 2:

输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

提示:

  • 1 <= n <= 200
  • n == isConnected.length
  • n == isConnected[i].length
  • isConnected[i][j] 为 1 或 0
  • isConnected[i][i] == 1
  • isConnected[i][j] == isConnected[j][i]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-provinces
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2. 方法一:图遍历

2.1 思路

        provinces翻译成“省份”实在是不得不服。好在这就是一个符号,不影响解题。

        本质上就是求一个图中的连通区域个数。

        这个图是由isConnected代表的邻接矩阵所表示。

        由于是全图遍历,所以广度优先搜索或者深度优先搜索都可以对付。本“图论基础专题”系列前面已经出现了好几道这样的题目。参考笨牛慢耕的Leetcode每日一解题解笔记(动态更新。。。)中“图论基础专题”。

        邻接矩阵的表示使得查询邻接节点非常方便。

2.2 代码实现

from typing import List
from collections import deque

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        if len(isConnected)==0:
            return 0
        
        N         = len(isConnected)
        islandCnt = 0
        q         = deque()
        visited   = set()
        for node in range(N):
            # print(node, visited)
            # find the next unvisited node
            if node not in visited:
                # print("Start node of the next island: ", node)
                # Start from this node to search the connected sub-graph
                q.append(node)
                visited.add(node)
                while len(q) > 0:
                    tmp = q.pop()
                    for k in range(N):
                        if isConnected[tmp][k]==1 and k not in visited:
                            q.append(k)
                            visited.add(k)
                islandCnt += 1
                
        return islandCnt        
    
if __name__ == '__main__':
    
    sln = Solution()
    
    isConnected = [[1,1,0],[1,1,0],[0,0,1]]
    print(sln.findCircleNum(isConnected))
    
    isConnected = [[1,0,0],[0,1,0],[0,0,1]]
    print(sln.findCircleNum(isConnected))

        以上代码中用的是“q.pop()”,这个与q.append()配对,表示把q当作栈使用,因此代表卓深度优先搜索。而如果改为“q.popleft()”则代表把q当作队列使用,这样就编程了广度优先搜索。 

        执行用时:44 ms, 在所有 Python3 提交中击败了88.42%的用户

        内存消耗:15.4 MB, 在所有 Python3 提交中击败了42.50%的用户

3. 方法二:并查集

3.1 思路

        计算连通分量数的另一个方法是使用并查集(union-find)。初始时,每个省都初始化属于不同的连通分量。遍历矩阵isConnected,如果两个省之间有相连关系,则它们属于同一个连通分量,对它们进行合并。

        遍历矩阵isConnected 的全部元素之后,剩下的连通分量的总数,即为省份的总数。

        在以下实现中,以groupID存储对应节点所属组的ID号。初始化时将每个省初始化为只包含它自己的组。groupID也就是各省自己的ID或者索引号。

        然后从小到大遍历,对于每个省份,查询它邻接的节点中索引号最大的,并将它自己归并到该索引号最大的省所属group。最终,group号没有发生变化的省的个数就是最后剩下的group数,也即本题所求的省份数。

3.2 代码实现

class Solution:
    def findCircleNum_union_find(self, isConnected: List[List[int]]) -> int:
        def find(index: int) -> int:
            # Find the group to which the index belongs
            if groupID[index] != index:
                groupID[index] = find(groupID[index])
            return groupID[index]
        
        def union(index1: int, index2: int):
            # Join index1 into the group which index2 belongs to
            groupID[find(index1)] = find(index2)
        
        # Initialization.
        # Start from the state in which each province belongs to the own-group
        # Each group has one province with the same index as its initial root.
        groupID      = list(range(len(isConnected)))
        
        # For each group, find the (directly or indirectly) connected province and
        # join itself into the later group
        for i in range(len(isConnected)):
            for j in range(i + 1, len(isConnected)):
                if isConnected[i][j] == 1:
                    union(i, j)
        
        numGroup = sum(groupID[i] == i for i in range(len(isConnected)))
        return numGroup

        执行用时:56 ms, 在所有 Python3 提交中击败了44.57%的用户

        内存消耗:15.3 MB, 在所有 Python3 提交中击败了54.80%的用户

        回到主目录:笨牛慢耕的Leetcode每日一解题解笔记(动态更新。。。)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值