Leetcode 1584. Min Cost to Connect All Points (python)

博客介绍了LeetCode 1584题目的解法,强调错误的直觉判断,并详细解释了如何利用最小生成树(Minimum Spanning Tree, MST)解决问题。通过并查集(Union Find)实现MST,指出当边的数量等于点的数量减一时,所有点已联通。同时,博主分享了二刷经验,提醒在判断两点联通时应首选Union Find,避免使用BFS导致超时。最后,对比了C++排序版本和Python堆版本的效率差异。" 112176059,10545636,Vaex:Python数据分析利器,1秒处理100万行HDF5数据,"['Python', '数据分析', 'HDF5', '大数据', '性能优化']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Leetcode 1584. Min Cost to Connect All Points

题目

在这里插入图片描述
在这里插入图片描述

错误解法:

最开始以为通过判断加入的边的数量和被访问过的点的数量可以判断是不是符合条件了,其实这样是不成立的,可以举出很多反例

class Solution(object):
    def minCostConnectPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        d2p = collections.defaultdict(list)
        d_vec = []
        for i in range(len(points)):
            for j in range(i+1,len(points)):
                p1 = points[i]
                p2 = points[j]
                dis = abs(p1[0]-p2[0])+abs(p1[1]-p2[1])
                d2p[dis].append((i,j))
                d_vec.append(dis)
        
        #print(d2p)
        d_vec.sort()
        #print(d_vec)
        visited = set()
        ans = 0
        for dis in d_vec:
            for pair in d2p[dis]:
                if pair[0] not in visited or pair[1] not in visited:
                    ans += dis
                    visited.add(pair[0])
                    visited.add(pair[1])
                    #print(points[pair[0]],points[pair[1]])
                    #print(dis)
                if len(visited)==len(points):
                    return ans
        return 0

解法:Minimum spanning tree

其实这个题目是最小生成树的一个典型代表。给一系列的点,spanning tree指的是能将他们全部联通的情况。而minimum在这道题目指的是所有spanning tree中边的和最小的情况
参考这里:https://www.hackerearth.com/practice/algorithms/graphs/minimum-spanning-tree/tutorial/

这边实现minimum spanning可以使用union find。因为当我们考虑一条边时,是不是加入这条边的判断是基于他们是否在一个连通图中,或者一个cluster中,这个很显然可以用并查集

这边值得一提的是,如果当我们加入的边等于点数减1的时候,因为我们使用了并查集,所以可以保证所有的点都已经被联通了。因为对于几个点,把他们联通的最小边数是点数减一

union find + sort

class Solution(object):
    def minCostConnectPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        # standard definition of union find
        def find(p):
            while p != roots[p]:
                p = roots[p]
            return p
        def union(p,q):
            roots[find(p)] = find(q)
            
        roots = list(range(len(points)))
        
        graph = []
        for i in range(len(points)):
            for j in range(i+1,len(points)):
                dis = abs(points[i][0]-points[j][0]) + abs(points[i][1]-points[j][1])
                graph.append((i,j,dis))
        
        # sort the graph
        graph.sort(key=lambda x:x[2])
        cost = 0
        num_edge = 0
        for p,q,dis in graph:
            if num_edge == len(points)-1:
                break
            # if p and q is already connected, we skip this edge
            if find(p)==find(q):
                continue
            # if q and q are not connected, we connect them and add to cost
            union(p,q)
            cost += dis
            num_edge += 1
        return cost

union find + heap

class Solution(object):
    def minCostConnectPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        # standard definition of union find
        def find(p):
            while p != roots[p]:
                p = roots[p]
            return p
        def union(p,q):
            roots[find(p)] = find(q)
            
        roots = list(range(len(points)))
        
        graph = []
        for i in range(len(points)):
            for j in range(i+1,len(points)):
                dis = abs(points[i][0]-points[j][0]) + abs(points[i][1]-points[j][1])
                # put dis first for the heapify process
                graph.append((dis,i,j))
        
        # the heap the constructed according to the dis
        heapq.heapify(graph)
        cost = 0
        num_edge = 0
        # when the num_edge equal to the num_points-1, meaning that we are done
        while num_edge < len(points)-1:
            dis,p,q = heapq.heappop(graph)
            # if p and q is already connected, we skip this edge
            if find(p)==find(q):
                continue
            # if q and q are not connected, we connect them and add to cost
            union(p,q)
            cost += dis
            num_edge += 1
        return cost

二刷经验

二刷的时候还是没有直接想到union find,直接用了个bfs来判断两个点是否联通了,显然是tle的

class Solution:
    def minCostConnectPoints(self, points: List[List[int]]) -> int:
        def man_dist(p1,p2):
            return abs(p1[0]-p2[0]) + abs(p1[1]-p2[1])
        def change2hashable(p):
            return str(p[0]) + str(p[1])
        def is_connected(p1,p2,G):
            if len(G) == 0:
                return False
            q = collections.deque()
            q.append(p1)
            visited = {p1:True}
            while q:
                curr = q.popleft()
                if curr == p2:
                    return True
                for nei in G[curr]:
                    if nei not in visited:
                        q.append(nei)
                        visited[nei] = True
            return False
            
        
        n = len(points)
        dist_list = []
        for i in range(n):
            for j in range(i+1,n):
                p1,p2 = points[i],points[j]
                dist = man_dist(p1,p2)
                dist_list.append([dist,change2hashable(p1),change2hashable(p2)])
        
        dist_list.sort(key = lambda x:x[0])
        # print(dist_list)
        ans = 0
        G = collections.defaultdict(list)
        connected = {}
        for dist,p1,p2 in dist_list:
            if p1+'+'+p2 in connected or p2+'+'+p1 in connected:
                continue
            else:
                if is_connected(p1,p2,G):
                    continue
            ans += dist
            G[p1].append(p2)
            G[p2].append(p1)
            connected[p1+'+'+p2] = True
            connected[p2+'+'+p1] = True

        
        return ans

判断两个点在一个图上是否联通一定第一时间想到union find!!!。union find的本质是把图变成了一棵树,所以查找的复杂度是这棵树的高度,但是bfs需要访问图上每个节点,显然复杂度高很多

C++版本
用排序解出来的直接tle了,照理来说跟用heap的复杂度是一样的

class Solution {
public:
    vector<int> root;
    int find(int p){
        while(root[p]!=p){
            p = root[p];
        }
        return p;
    }
    void union_(int p,int q){
        root[find(p)] = find(q);
    }
    int man_dist(vector<int>& p1,vector<int>& p2){
        return abs(p1[0]-p2[0]) + abs(p1[1]-p2[1]);
    }
    static bool comparer(vector<int>& a, vector<int>& b){
        return a[2] < b[2];
    }
    int minCostConnectPoints(vector<vector<int>>& points) {
        // get all the distance and sort
        int n = points.size();
        vector<vector<int>> edges;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                edges.push_back({i,j,man_dist(points[i],points[j])});
            }
        }
        sort(edges.begin(),edges.end(),comparer);
        for(int i=0;i<n;i++) root.push_back(i);
        int ans = 0;
        int num_edge = 0;
        for(auto& edge : edges){
            int p1 = edge[0];
            int p2 = edge[1];
            if(find(p1) == find(p2)) continue;
            ans += edge[2];
            union_(p1,p2);
            num_edge++;
            if(num_edge == n-1) break;
            
        }
        return ans;
    }
};

heap版本

class Solution {
public:
    vector<int> root;
    int find(int p){
        while(root[p]!=p){
            p = root[p];
        }
        return p;
    }
    void union_(int p,int q){
        root[find(p)] = find(q);
    }
    int man_dist(vector<int>& p1,vector<int>& p2){
        return abs(p1[0]-p2[0]) + abs(p1[1]-p2[1]);
    }
    static bool comparer(vector<int>& a, vector<int>& b){
        return a[2] < b[2];
    }
    int minCostConnectPoints(vector<vector<int>>& points) {
        // get all the distance and sort
        int n = points.size();
        priority_queue<vector<int>> edges;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                edges.push({-1*man_dist(points[i],points[j]),i,j});
            }
        }
        // sort(edges.begin(),edges.end(),comparer);
        for(int i=0;i<n;i++) root.push_back(i);
        int ans = 0;
        int num_edge = 0;
        // for(auto& edge : edges){
        
        while(num_edge != n-1){
            vector<int> edge = edges.top();
            edges.pop();
            int p1 = edge[1];
            int p2 = edge[2];
            if(find(p1) == find(p2)) continue;
            // cout << "1111" << endl;
            ans -= edge[0];
            union_(p1,p2);
            num_edge++;
            // if(num_edge == n-1) break;
            
        }
        return ans;
    }
};

时间复杂度:O(n^2 * log(n^2 )),log(n^2)来源于每一次查找,需要查找你n*n次
空间复杂度:O(n^2)
参考:
https://www.cnblogs.com/gczr/p/12077934.html
https://www.hackerearth.com/practice/notes/disjoint-set-union-union-find/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值