1. 程式人生 > 其它 >LeetCode 547. 省份數量 | Python

LeetCode 547. 省份數量 | Python

技術標籤:LeetCodedfsleetcodebfs並查集python

547. 省份數量


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/number-of-provinces/

題目


n 個城市,其中一些彼此相連,另一些沒有相連。如果城市 a 與城市 b 直接相連,且城市 b 與城市 c 直接相連,那麼城市 a 與城市 c 間接相連。

省份 是一組直接或間接相連的城市,組內不含其他沒有相連的城市。

給你一個 n x n 的矩陣 isConnected ,其中 isConnected[i][j] = 1 表示第 i 個城市和第 j 個城市直接相連,而 isConnected[i][j] = 0

表示二者不直接相連。

返回矩陣中 省份 的數量。

示例 1:

img

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

示例 2:

img

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

提示:

  • 1 <= n <= 200
  • n == isConnected.length
  • n == isConnected[i].length
  • isConnected[i][j]10
  • isConnected[i][i] == 1
  • isConnected[i][j] == isConnected[j][i]

解題思路


思路:深度優先搜尋、廣度優先搜尋、並查集

先審題,題目中給定 n n n 個城市,一些彼此相連,一些沒有相連。其中有個 省會 的概念,由一組直接或間接相連的城市組成,組內不存在其他沒有相連的城市。現在題目要求,省會 的數量?

在這裡,我們可以將城市之間的相連關係看成是圖,矩陣 i s C o n n e c t e d isConnected isConnected 是圖的鄰接矩陣,城市是圖的節點,相連關係是圖中相連的邊,而要求的省會即是圖中的連通分量,那麼問題就變成求圖中連通分量的數量。

那麼我們可以考慮使用深度優先搜尋、廣度優先搜尋、並查集。

深度優先搜尋

先說下深度優先搜尋的做法:

  • 先宣告變數 v i s i t e d visited
    visited
    用以標記城市是否被遍歷訪問過;
  • 遍歷所有的城市,若遍歷的城市未被訪問,那麼以此開始深度優先搜尋,同時進行標記,直到同一個連通分量中的點都被訪問,那表示存在一個省份。
  • 若此時還有其他城市未被訪問,那麼表示有新的連通分量,用 c o u n t count count 儲存。繼續搜尋,直到所有的城市都被訪問。最終返回 c o u n t count count

具體的程式碼實現如下。

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        def dfs(i):
            """以 i 為起點開始深度優先搜尋
            """
            # 先進行標記
            visited[i] = 1
            # 繼續搜尋與 i 相連的城市
            for j in range(n):
                if isConnected[i][j] == 1 and not visited[j]:
                    dfs(j)


        # 城市數量
        n = len(isConnected)
        # visited 用以標記城市是否被訪問
        visited = [0] * n
        # 用以儲存省會的數量
        count = 0
        # 開始遍歷城市進行搜尋
        for i in range(n):
            # 城市未被訪問時,開始搜尋
            if not visited[i]:
                count += 1
                dfs(i)
        
        return count
            

廣度優先搜尋

廣度優先搜尋的思路,與上面深度優先搜尋的思路類似。用 v i s i t e d visited visited 標記城市是否曾被訪問,遍歷城市,若城市未被訪問,那麼就由該城市開始進行廣度優先搜尋,知道同一分量的所有城市都被訪問,可得到一個省會的數量,用 c o u n t count count 儲存。直到所有的城市都被訪問,可得省會的總數,返回 c o u n t count count

具體的程式碼實現如下。

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        # 標記
        visited = [0] * n

        count = 0
        queue = collections.deque()

        # 遍歷城市,開始搜尋
        for i in range(n):
            if not visited[i]:
                # 更新變數
                visited[i] = 1
                count += 1
                queue.append(i)

                while queue:
                    # 出隊開始搜尋
                    u = queue.popleft()
                    # 搜尋與 u 相連且未被訪問的城市
                    for v in range(n):
                        if isConnected[u][v] == 1 and not visited[v]:
                            visited[v] = 1
                            queue.append(v)
            
        return count

並查集

現在問題變成求圖中連通分量的數量,我們可以使用並查集的方法。

n n n 個城市表示圖的 n n n 個點,初始時,令所有的點都指向自身,自成一個連通分量。

遍歷矩陣 i s C o n n e c t e d isConnected isConnected,如果點與點之間存在相連的關係,那麼它們屬於同一個連通分量,將點與其鄰接點進行合併。

當所有點都遍歷後,計算合併後連通分量的總數,返回。

具體的程式碼實現如下。

class UnionFind:
    """並查集
    """
    def __init__(self, n):
        self.parent = [i for i in range(n)]
        # 初始連通分量數為 n 個
        self.count = n
    
    def find(self, x):
        if x != self.parent[x]:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, u, v):
        u_root = self.find(u)
        v_root = self.find(v)
        # 合併,連通分量數減少
        if u_root != v_root:
            self.parent[u_root] = v_root
            self.count -= 1

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        uf = UnionFind(n)
        for i in range(n):
            for j in range(i+1, n):
                if isConnected[i][j] == 1:
                    # 有相連關係,則進行合併
                    uf.union(i, j)
        return uf.count

歡迎關注


公眾號 【書所集錄


如有錯誤,煩請指正,歡迎指點交流。若覺得寫得還不錯,麻煩點個贊