1. 程式人生 > 其它 >力扣 leetcode 1584. 連線所有點的最小費用 (python)並查集加Kruskal演算法

力扣 leetcode 1584. 連線所有點的最小費用 (python)並查集加Kruskal演算法

技術標籤:pythonleetcode演算法pythonleetcode資料結構kruskal

Topic

給你一個points 陣列,表示 2D 平面上的一些點,其中 points[i] = [xi, yi] 。
連線點 [xi, yi] 和點 [xj, yj] 的費用為它們之間的 曼哈頓距離 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的絕對值。
請你返回將所有點連線的最小總費用。只有任意兩點之間 有且僅有 一條簡單路徑時,才認為所有點都已連線。

Example_1

這裡是引用
輸入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]]

輸出:20
解釋:
在這裡插入圖片描述
我們可以按照上圖所示連線所有點得到最小總費用,總費用為 20 。
注意到任意兩個點之間只有唯一一條路徑互相到達。

Example_2

輸入:points = [[3,12],[-2,5],[-4,1]]
輸出:18

Example_3

輸入:points = [[0,0],[1,1],[1,0],[-1,1]]
輸出:4

Example_4

輸入:points = [[-1000000,-1000000],[1000000,1000000]]
輸出:4000000

Example_5

輸入:points = [[0,0]]
輸出:0

Solution:

熟悉並查集和Kruskal演算法後很簡單就可以看出這是一道典型的Kruskal演算法題

不熟悉的同志可以看我之前總結的並查集+Kruskal演算法

首先要構造權重邊計算權重:
因為Kruskal演算法以邊為維度
因此需要首先構造所有點兩兩相連時每條邊的權重(這裡是邊的長度)
同時需要將points中原來的邊和權重放在一起放入edges中
之後需要按照權重進行排序

最後通過並查集計算
先例項化並查集並在並查集中構造節點
然後使用並查集構造連通分量

若兩個節點已經連線了,跳過
若兩個節點未連線
將兩個點連線起來
將花費加入結果中

最後返回res即是結果

Code_1

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 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 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 class Solution: def minCostConnectPoints(self, points: List[List[int]]) -> int: if not points: return 0 n = len(points) # 根據Kruskal演算法三部曲: # 1、構造權重邊:因為Kruskal演算法以邊為維度,因此需要首先構造所有點兩兩相連時每條邊的權重,在這裡就是邊的長度。 edges = [] # 這裡需要兩重迴圈,但是需要注意,第二層迴圈需要以第一層迴圈為起點(兩兩相連嘛) for node1 in range(n): for node2 in range(node1, n): p1 = points[node1] p2 = points[node2] # 這裡需要計算權重,也就是邊的長度 edge = (node1, node2, abs(p1[0 ] - p2[0]) + abs(p1[1] - p2[1])) edges.append(edge) # 2、排序:對所有的邊按照權重從小到大排序(題目要求是最小的花費) edges.sort(key=lambda item: item[-1]) # 3、生成最小生成樹:Kruskal是以並查集演算法為基礎的,因此這裡就是通過並查集來構造聯通分量。 uf = UnionFind() res = 0 # 在並查集中構造節點 for i in range(n): uf.add(i) # 使用並查集構造聯通分量 for node1, node2, dest in edges: if uf.is_connected(node1, node2): # 如果兩個節點已經連線了,跳過 continue # 將花費加入結果中 res += dest # 將兩個點連線起來 uf.merge(node1, node2) return res

Result_1:

在這裡插入圖片描述

optimize

程式碼中僅使用了路徑壓縮
我們同樣可以使用按秩合併(並查集中size的優化)進行優化

size優化我之前總結了模板可供參考

Code_2

class UnionFind:
    def __init__(self, n):
        self.father = {}
        self.size = [0] * n

    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:
            return False

        if self.size[root_x] < self.size[root_y]:
            root_x, root_y = root_y, root_x

        self.size[root_x] += self.size[root_y]
        self.father[root_x] = root_y

    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

class Solution:
    def minCostConnectPoints(self, points: List[List[int]]) -> int:
        if not points:
            return 0

        n = len(points)
        # 根據Kruskal演算法三部曲:
        # 1、構造權重邊:因為Kruskal演算法以邊為維度,因此需要首先構造所有點兩兩相連時每條邊的權重,在這裡就是邊的長度。
        edges = []
        # 這裡需要兩重迴圈,但是需要注意,第二層迴圈需要以第一層迴圈為起點(兩兩相連嘛)
        for node1 in range(n):
            for node2 in range(node1, n):
                p1 = points[node1]
                p2 = points[node2]
                # 這裡需要計算權重,也就是邊的長度
                edge = (node1, node2, abs(p1[0 ] - p2[0]) + abs(p1[1] - p2[1]))
                edges.append(edge)
        
        # 2、排序:對所有的邊按照權重從小到大排序(題目要求是最小的花費)
        edges.sort(key=lambda item: item[-1])

        # 3、生成最小生成樹:Kruskal是以並查集演算法為基礎的,因此這裡就是通過並查集來構造聯通分量。
        uf = UnionFind(n)
        res = 0

        # 在並查集中構造節點
        for i in range(n):
            uf.add(i)

        # 使用並查集構造聯通分量
        for node1, node2, dest in edges:
            if uf.is_connected(node1, node2):
                # 如果兩個節點已經連線了,跳過
                continue
            # 將花費加入結果中
            res += dest
            # 將兩個點連線起來
            uf.merge(node1, node2)

        return res

Result_2:

即使leetcode執行時間誤差較大
但由於兩者取得都是近乎最佳執行效率的情況
可以看出還是有一定執行效率的提高的

在這裡插入圖片描述