547. Number of Provinces

547. Number of Provinces

這個題目是有 n 個島嶼,其中島嶼可能會相連,我們要知道是不是相連的方式是題目有給定一個 n x n 的矩陣,如果矩陣的位置 i, j 的值是 1 ,代表ij 小島是有相鄰的。

這題我一開始想的方向是,其實題目已經告訴我們相連的方式了,那我就從矩陣一開始的座標 (0, 0) 一個一個走下去,所以我就寫了兩個 for 迴圈如下,我想到的邏輯大致上就是我就開始探索,題目給出的條件也都很明確,我搜尋的路上只要有看到其他的島嶼相連,我就繼續跳過去,再慢慢走。

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        visited = set()
        count = 0
        for i in range(n):
            for j in range(n):
                # 開始走訪各個島嶼
                ...

這樣的做法應該也是有辦法做出來的,可是如果一開始就用兩個迴圈,像是我們造訪過哪些「座標」的方式來走訪各個島嶼的話,真的會暈頭轉向。

後來才想到,其實我們要專注的應該是「哪個」島嶼已經被造訪過了,總共只有 n 個島嶼需要造訪,所以我們需要的是一個長度 n 初始值為 False 的陣列。

走訪的規則很簡單,以第一個島嶼為例,如果第一個島嶼的那列 isConnected[0]每一個數字都是 1 ,代表的就是一號島嶼跟每個島嶼都相連,那我們就從第一個島嶼出發,往這一列的右邊去走,中間如果發現其他島嶼,就走過去看,然後就回回到跟第一個島嶼一樣的邏輯,繼續出發到沒有島嶼可以探索為止。中間就要記錄我們走訪過哪些島嶼。

DFS

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        visited = [False] * n
        count = 0
        def dfs(i):
            # 走訪看看島嶼 i 是否與其他島嶼有連結
            for j in range(n):
                # 如果有連結,在之前也還沒看過的話,走過去拜訪
                if isConnected[i][j] == 1 and visited[j] == False:
                    visited[i] = True
                    dfs(j)

        # 走訪每一個島嶼
        for i in range(n):
            if visited[i] == False:
                visited[i] = True
                dfs(i)
                count += 1

        return count

時間複雜度:O(N^2)

空間複雜度:O(N)

BFS

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n = len(isConnected)
        visited = [False] * n
        count = 0
        queue = deque([])
        for i in range(n):
            if visited[i] == False:
                queue.append(i)
                while queue:
                    x = queue.popleft()
                    visited[x] = True
                    for j in range(n):
                        if isConnected[x][j] == 1 and visited[j] == False:
                            queue.append(j)
                count += 1
        return count

UnionFind

最後一種方法需要使用到 Graph 圖 Disjoint Set的觀念

class UnionFind:
    def __init__(self, size: int):
        self.size = size
        self.root = [i for i in range(size)]
    
    def find(self, x):
        return self.root[x]
    
    def union(self, x, y):
        xRoot = self.find(x)
        yRoot = self.find(y)
        if xRoot != yRoot:
            for i in range(self.size):
                if yRoot == self.root[i]:
                    self.root[i] = xRoot

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        
        n = len(isConnected)
        
        unionFind = UnionFind(n)
        
        for i in range(n):
            for j in range(i + 1, n):
                if isConnected[i][j] == 1:
                    unionFind.union(i, j)
                    
        s = set(unionFind.root)
        return len(s)