1584-min-cost-to-connect-all-points

Try it on leetcode

Description

You are given an array points representing integer coordinates of some points on a 2D-plane, where points[i] = [xi, yi].

The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val.

Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points.

 

Example 1:

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
Output: 20
Explanation: 

We can connect the points as shown above to get the minimum cost of 20.
Notice that there is a unique path between every pair of points.

Example 2:

Input: points = [[3,12],[-2,5],[-4,1]]
Output: 18

 

Constraints:

  • 1 <= points.length <= 1000
  • -106 <= xi, yi <= 106
  • All pairs (xi, yi) are distinct.

Solution(Python)

class UnionFind:
    def __init__(self, size):
        self.group = [0] * size
        self.rank = [0] * size

        for i in range(size):
            self.group[i] = i

    def find(self, a):
        if a != self.group[a]:
            self.group[a] = self.find(self.group[a])
        return self.group[a]

    def union(self, a, b):
        groupA = self.find(a)
        groupB = self.find(b)

        if groupA == groupB:
            return False

        if self.rank[groupA] > self.group[groupB]:
            self.group[groupB] = groupA
        elif self.rank[groupB] > self.group[groupA]:
            self.group[groupA] = groupB
        else:
            self.group[groupB] = groupA
            self.rank[groupA] += 1
        return True

    def isconnected(self, a, b):
        return self.find(a) == self.find(b)


class Solution:
    def minCostConnectPoints(self, points: List[List[int]]) -> int:
        return self.optimized_prims(points)

    # Time Complexity: O(n2 log(n))
    # space complexity: o(n^2)
    def kruskar(self, points: List[List[int]]) -> int:
        n = len(points)
        all_edges = []

        for cur_node in range(n):
            for next_node in range(cur_node + 1, n):
                weight = abs(points[cur_node][0] - points[next_node][0]) + abs(
                    points[cur_node][1] - points[next_node][1]
                )
                all_edges.append((weight, cur_node, next_node))
        all_edges.sort()

        uf = UnionFind(n)
        mst_cost = 0
        edges_used = 0

        for w, p1, p2 in all_edges:
            if uf.union(p1, p2):
                mst_cost += w
                edges_used += 1
                if edges_used == n - 1:
                    break

        return mst_cost

    # Time Complexity: O(n2 log(n))
    # space complexity: o(n^2)
    def naiveprims(self, points: List[List[int]]) -> int:
        n = len(points)
        heap = [(0, 0)]

        in_mst = [False] * n

        mst_cost = 0
        edges_used = 0

        while edges_used < n:
            weight, cur_node = heapq.heappop(heap)

            if in_mst[cur_node]:
                continue
            in_mst[cur_node] = True
            mst_cost += weight
            edges_used += 1

            for next_node in range(n):
                if not in_mst[next_node]:
                    next_weight = self.manhattan_dist(
                        points[cur_node], points[next_node]
                    )
                    heapq.heappush(heap, (next_weight, next_node))

        return mst_cost

    def manhattan_dist(self, point1, point2):
        return abs(point1[0] - point2[0]) + abs(point1[1] - point2[1])

    # Time Complexity: O(n^2)
    # space complexity: o(n)
    def optimized_prims(self, points: List[List[int]]) -> int:
        n = len(points)
        mst_cost = 0
        edges_used = 0

        in_mst = [False] * n
        min_dist = [math.inf] * n
        min_dist[0] = 0

        while edges_used < n:
            cur_min_edge = math.inf
            cur_node = -1

            for node in range(n):
                if not in_mst[node] and cur_min_edge > min_dist[node]:
                    cur_min_edge = min_dist[node]
                    cur_node = node
            mst_cost += cur_min_edge
            edges_used += 1
            in_mst[cur_node] = True

            for next_node in range(n):
                weight = self.manhattan_dist(
                    points[cur_node], points[next_node])
                if not in_mst[next_node] and min_dist[next_node] > weight:
                    min_dist[next_node] = weight

        return mst_cost