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/