這道題我們需要用並查集進行解決,但是什麼是並查集?正如他的名字一樣並查集是一種資料結構。他有什麼好處吶?這裡他的合併,查詢的時間複雜度都是只有o(1), 速度特別快,所以有時候還是需要使用並查集
並查集這三個字,乙個字代表乙個意思。
並(union),代表合併
查(find),代表查詢
集(set),代表這是乙個以字典為基礎的資料結構,它的基本功能是合併集合中的元素,查詢集合中的元素。
並查集跟樹有些類似,只不過她跟樹是相反的。在樹這個資料結構裡面,每個節點會記錄它的子節點。在並查集裡,每個節點會記錄它的父節點。並查集主要操作是合併與查詢,它是把初始不相交的集合經過多次合併操作後合併為乙個大集合,然後可以通過查詢判斷兩個元素是否已經在同乙個集合中了。
既然是一種資料結構,用python該如何進行解決吶?
首先我們先定義乙個並查集的類,我們之前說了,只記錄父節點,那我們應該每個裡面只包含自己的父節點,
class
unionfind
:def
__init__
(self)
:"""
記錄每個節點的父節點
"""self.father =
之後我們開始他的用法的寫出,首先是查詢:
def
find
(self, x)
:"""
查詢根節點
路徑壓縮
"""root = x
while self.father[root]
!=none
: root = self.father[root]
# 路徑壓縮
while x != root:
original_father = self.father[x]
self.father[x]
= root
x = original_father
return root
如果我們乙個乙個查詢的話,如果他是這種形式的話,時間複雜度就會變成o(n)!
這時候我們就採用路徑壓縮的方法來進行解決,顧名思義,我們就是把中間的過程全部縮減為每個直接指向自己公共的祖先。
接下來就是合併了,合併兩棵樹的操作可以簡單的規定讓右邊的樹的根結點指向左邊樹的根結點。具體哪棵樹接其實並沒有太大的影響。
def
merge
(self, x, y, val)
: root_x, root_y = self.find(x)
, self.find(y)
if root_x != root_y:
self.father[root_x]
= root_y
之後啊,還有新增節點功能,節點就是祖先,所以他的爸爸沒有,也就是這樣
def
add(self, x)
:if x not
in self.father:
self.father[x]
=none
還有乙個功能就是判斷兩個節點是否相連,
def
is_connected
(self, x, y)
:"""
判斷兩節點是否相連
"""return self.find(x)
== self.find(y)
就是看祖先是否相同。
我們來看題目該怎麼進行解決,我們可以把所有城市都設定乙個初始的集合,
uf = unionfind(
)for i in
range
(len
(m))
: uf.add(i)
之後我們就要開始進行判斷是否連線,是一就連線,我們就把他就進行連線好
for j in
range
(i):
if m[i]
[j]:
uf.merge(i,j)
之後我們就查剩下的集合數目就好了,但是我們對集合數沒有操作啊?我們可以在定義的並查集中新增一次加乙個集合,連線一次減乙個集合,完整**如下:
class
unionfind
:def
__init__
(self)
: self.father =
# 額外記錄集合的數量
self.num_of_sets =
0def
find
(self,x)
: root = x
while self.father[root]
!=none
: root = self.father[root]
while x != root:
original_father = self.father[x]
self.father[x]
= root
x = original_father
return root
defmerge
(self,x,y)
: root_x,root_y = self.find(x)
,self.find(y)
if root_x != root_y:
self.father[root_x]
= root_y
# 集合的數量-1
self.num_of_sets -=
1def
add(self,x)
:if x not
in self.father:
self.father[x]
=none
# 集合的數量+1
self.num_of_sets +=
1class
solution
:def
findcirclenum
(self, m: list[list[
int]])
->
int:
uf = unionfind(
)for i in
range
(len
(m))
: uf.add(i)
for j in
range
(i):
if m[i]
[j]:
uf.merge(i,j)
return uf.num_of_sets
leetcode547(省份數量 並查集)
有 n 個城市,其中一些彼此相連,另一些沒有相連。如果城市 a 與城市 b 直接相連,且城市 b 與城市 c 直接相連,那麼城市 a 與城市 c 間接相連。省份 是一組直接或間接相連的城市,組內不含其他沒有相連的城市。給你乙個 n x n 的矩陣 isconnected 其中 isconnected...
省份數量 深度優先搜尋 廣度優先搜尋 並查集解法
547.省份數量 有n個城市,其中一些彼此相連,另一些沒有相連。如果城市a與城市b直接相連,且城市b與城市c直接相連,那麼城市a與城市c間接相連。省份是一組直接或間接相連的城市,組內不含其他沒有相連的城市。給你乙個n x n的矩陣isconnected,其中isconnected i j 1表示第i...
力扣305,島嶼數量,並查集。
島嶼數量 ii 主要的想法是用並查集,改一下並查集的 就好了。假設每個相鄰的兩個格仔之間有條邊。整體的 感覺還能再優化些。首先將建個m n長的陣列parent,表示每個格仔,m n長的陣列rankx用於路徑壓縮。尋根的函式若parent i i那麼就代表這個i位置就是乙個根節點。find root函...