力扣 leetcode 1319. 連通網路的操作次數 (python)並查集模板快速解及樹的高效解
技術標籤:pythonleetcodepython演算法leetcode資料結構並查集
Topic:
用乙太網線纜將 n 臺計算機連線成一個網路,計算機的編號從 0 到 n-1。線纜用 connections 表示,其中 connections[i] = [a, b] 連線了計算機 a 和 b。
網路中的任何一臺計算機都可以通過網路直接或者間接訪問同一個網路中其他任意一臺計算機。
給你這個計算機網路的初始佈線 connections,你可以拔開任意兩臺直連計算機之間的線纜,並用它連線一對未直連的計算機。請你計算並返回使所有計算機都連通所需的最少操作次數。如果不可能,則返回 -1 。
Example_1:
輸入:n = 4, connections = [[0,1],[0,2],[1,2]]
輸出:1
解釋:拔下計算機 1 和 2 之間的線纜,並將它插到計算機 1 和 3 上。
Example_2:
輸入:n = 6, connections = [[0,1],[0,2],[0,3],[1,2],[1,3]]
輸出:2
Example_3:
輸入:n = 6, connections = [[0,1],[0,2],[0,3],[1,2]]
輸出:-1
解釋:線纜數量不足。
Example_4:
輸入:n = 5, connections = [[0,1],[0,2],[3,4],[2,3]]
輸出:0
Solution_1:
本方法基於並查集模板實現
首先判斷不可能連通所有計算機的情況
如果連線數小於計算機數 - 1則不可能連通所有計算機
直接輸出 - 1
完成排除所有的不可能連通的情況
我們無需關心如何進行拆線再連線的過程
後面的只要多存在一個連通分量,就有一條多餘的邊需要操作
則我們需要計算連通分量的個數
計算連通分量:
我們可以通過模板中新增size計算連通分量:
在初始化並查集時同時初始化size為0
每增加一個新節點就增加一個連通分量
同時每合併兩個節點就減少一個連通分量
最後直接返回連通分量個數−1即可完成
Code_1:
class UnionFind:
def __init__(self):
self.father = {}
self.size = 0
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
def merge(self, x, y):
root_x,root_y = self.find(x),self.find(y)
if root_x != root_y:
self.father[root_x] = root_y
self.size -= 1
def is_connected(self, x, y):
return self.find(x) == self.find(y)
def add(self, x):
if x not in self.father:
self.father[x] = None
self.size += 1
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) < n - 1:
return -1
uf = UnionFind()
for j in range(n):
uf.add(j)
for i in connections:
if uf.is_connected(i[0], i[1]) is False:
uf.merge(i[0], i[1])
if uf.size == 1:
return 0
else:
return uf.size - 1
Result_1:
Solution_2:
第一步與思路一相同:
首先判斷不可能連通所有計算機的情況
如果連線數小於計算機數 - 1則不可能連通所有計算機
直接輸出 - 1
完成排除所有的不可能連通的情況
本思路中並查集是通過list實現的
總體上還是通過記錄連通分量來實現演算法的
先通過n個節點生成帶有節點序號的樹father用於記錄父節點
之後定義一個find_root函式去通過遞迴尋找根節點
只要father中記錄的父節點與此節點序號(索引)不相等
就去尋找此節點序號記錄的上一個節點的父節點
直至找到作為根節點的節點序號(節點序號與father中記錄的父節點相同)
如果根節點值不相等(處於不同的連通分量中)
則將其中一個節點的父節點賦值為另一個節點(將兩個連通分量相連)
最後就完成了所有的已知連通分量的連線和計算
返回連通分量數減一即可完成
Code_2:
class Solution:
def makeConnected(self, n: int, connections: List[List[int]]) -> int:
if len(connections) + 1 < n:
return - 1
father = list(range(n))
def find(x):
if x != father[x]:
father[x] = find(father[x])
return father[x]
for x, y in connections:
x, y = find(x), find(y)
if x != y:
father[y] = x
n -= 1
return n - 1
Result_2:
Result_3:
> 如果將遞迴方法變為遞推方法
其效率還會進一步的提高
效果就非常理想了