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:
輸入: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]
解題思路
思路:深度優先搜尋、廣度優先搜尋、並查集
先審題,題目中給定 n n n 個城市,一些彼此相連,一些沒有相連。其中有個 省會 的概念,由一組直接或間接相連的城市組成,組內不存在其他沒有相連的城市。現在題目要求,省會 的數量?
在這裡,我們可以將城市之間的相連關係看成是圖,矩陣 i s C o n n e c t e d isConnected isConnected 是圖的鄰接矩陣,城市是圖的節點,相連關係是圖中相連的邊,而要求的省會即是圖中的連通分量,那麼問題就變成求圖中連通分量的數量。
那麼我們可以考慮使用深度優先搜尋、廣度優先搜尋、並查集。
深度優先搜尋
先說下深度優先搜尋的做法:
- 先宣告變數
v
i
s
i
t
e
d
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
歡迎關注
公眾號 【書所集錄】
如有錯誤,煩請指正,歡迎指點交流。若覺得寫得還不錯,麻煩點個贊