给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。
连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。
请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。
输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
输出:20
解释:
我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。
注意到任意两个点之间只有唯一一条路径互相到达。
输入:points = [[3,12],[-2,5],[-4,1]]
输出:18
输入:points = [[0,0],[1,1],[1,0],[-1,1]]
输出:4
输入:points = [[-1000000,-1000000],[1000000,1000000]]
输出:4000000
输入:points = [[0,0]]
输出:0
熟悉并查集和Kruskal算法后很简单就可以看出这是一道典型的Kruskal算法题
不熟悉的同志可以看我之前总结的并查集+Kruskal算法
首先要构造权重边计算权重:
因为Kruskal算法以边为维度
因此需要首先构造所有点两两相连时每条边的权重(这里是边的长度)
同时需要将points中原来的边和权重放在一起放入edges中
之后需要按照权重进行排序
最后通过并查集计算
先实例化并查集并在并查集中构造节点
然后使用并查集构造连通分量
若两个节点已经连接了,跳过
若两个节点未连接
将两个点连接起来
将花费加入结果中
最后返回res即是结果
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
代码中仅使用了路径压缩
我们同样可以使用按秩合并(并查集中size的优化)进行优化
size优化我之前总结了模板可供参考
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
即使leetcode运行时间误差较大
但由于两者取得都是近乎最佳运行效率的情况
可以看出还是有一定运行效率的提高的