快轉到主要內容
  1. LeetCode/

LeetCode 547: Number of Provinces

·2 分鐘· ·
LeetCode Union Find Medium
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試: 2025-05-04

  • 總花費時間:00:00.00

解題思路
#

經典的 Union-Find 題目。一開始我沒想到可以直接用 isConnected 的長度來建立 parentrank 陣列。我以為要先遍歷 isConnected 才知道有幾個節點,所以一開始用 dictionary 來記錄這些資訊。之後就變成經典的 Union-Find 做法:遍歷 isConnected,做 union 操作,最後就能找出所有聯通群組。

解法
#

Union Find
#

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        parents = {}
        rank = {}

        def find(x: int):
            root = parents.get(x, x)

            if root != x:
                parent = find(root)
                parents[x] = parent
                return parent  
            else:
                parents[x] = x
                return x

        def union(x, y):
            x_root = find(x)
            y_root = find(y)

            if x_root == y_root:
                return

            x_rank = rank.get(x_root, 1)
            y_rank = rank.get(y_root, 1)

            if x_rank > y_rank:
                rank[x_root] += y_rank
                parents[y_root] = x_root
            
            else:
                
                rank[y_root] = rank.get(y_root, 1) + x_rank
                parents[x_root] = y_root

        for i in range(len(isConnected)):
            for j in range(len(isConnected[0])):
                if isConnected[i][j]:
                    union(i, j)

        return len(set(find(i) for i in range(len(isConnected))))

Union Find w/ known the size
#

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        parents = [i for i in range(n)]
        rank = [1 for _ in range(n)]

        def find(x: int):
            root = parents[x]

            if root != x:
                parent = find(root)
                parents[x] = parent
                return parent
            else:
                parents[x] = x
                return x

        def union(x, y):
            x_root = find(x)
            y_root = find(y)

            if x_root == y_root:
                return

            x_rank = rank[x_root]
            y_rank = rank[y_root]

            if x_rank > y_rank:
                rank[x_root] += y_rank
                parents[y_root] = x_root
            
            else:
                
                rank[y_root] = rank[y_root] + x_rank
                parents[x_root] = y_root

        for i in range(len(isConnected)):
            for j in range(len(isConnected[0])):
                if isConnected[i][j]:
                    union(i, j)

        return len(set(find(i) for i in range(n)))

DFS
#

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        seen = set()
        ans = 0

        def dfs(x):
            for idx in range(n):
                if not isConnected[x][idx] or idx in seen:
                    continue
                seen.add(idx)
                dfs(idx)

        for idx in range(n):
            if idx not in seen:
                ans += 1
                dfs(idx)

        return ans

收穫
#

遇到的問題
#

一開始我用下面這段程式碼來算最後結果的集合大小:

return len(set(parents.values()))

但這裡有個盲點,就是在我們遍歷完所有連線關係後,parents 中的每個值不一定都是最終的 root node。舉例來說:

若連線如下:

  • a ↔ b
  • c ↔ d
  • d ↔ e
  • e ↔ b

當處理第一個連線時,可能會記錄 a 的 root 是 b,但在處理到最後一個連線後,b 的 root 變成了 e。此時如果直接用 parents.values() 去算會算錯。所以最後一定要對每個節點呼叫 find() 來確保取得正確的 root。

相關文章

LeetCode 684: Redundant Connection
·1 分鐘
LeetCode Daily Medium
LeetCode 解題紀錄
LeetCode 1094: Car Pooling
·1 分鐘
LeetCode Difference Array Medium
LeetCode 解題紀錄
LeetCode 1109: Corporate Flight Bookings
·1 分鐘
LeetCode Difference Array Medium
LeetCode 解題紀錄
LeetCode 3527: Find the Most Common Response
·1 分鐘
LeetCode Bi-Weekly Medium
LeetCode 解題紀錄
LeetCode 3528: Unit Conversion I
·1 分鐘
LeetCode Bi-Weekly Medium
LeetCode 解題紀錄
LeetCode 2799: Count Complete Subarrays in an Array
·1 分鐘
LeetCode Daily Medium
LeetCode 解題紀錄