问题描述
给你一个
points
数组,表示 2D 平面上的一些点,其中points[i] = [xi, yi]
。连接点
[xi, yi]
和点[xj, yj]
的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj|
,其中|val|
表示val
的绝对值。请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。
示例
示例 1:
输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]] 输出:20 解释:我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。 注意到任意两个点之间只有唯一一条路径互相到达。示例 2:
输入:points = [[3,12],[-2,5],[-4,1]] 输出:18示例 3:
输入:points = [[0,0],[1,1],[1,0],[-1,1]] 输出:4示例 4:
输入:points = [[-1000000,-1000000],[1000000,1000000]] 输出:4000000示例 5:
输入:points = [[0,0]] 输出:0提示:
1 <= points.length <= 1000
-10^6 <= xi, yi <= 10^6
- 所有点
(xi, yi)
两两不同。
问题分析:
记录一道经典Kruskal算法,思路很简单:首先生成一个边权图,根据已知节点计算权值,对对应权值进行排序。每次挑选最小权重的边进行连接,同时判断会不会形成环。说到这里其实已经很清晰了,并查集就是为这个问题服务的。具体见代码。
代码如下:
// 经典并查集模板
class UF {
vector<int> parent;
public:
UF(int n) {
for (int i = 0; i < n; ++i) {
parent.push_back(i);
}
}
int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void _union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
}
}
bool isConnected(int x, int y) { return find(x) == find(y); }
};
class Solution {
public:
int minCostConnectPoints(vector<vector<int>>& points) {
int n = points.size();
vector<vector<int>> edges;
// 建立边权图
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
int xi = points[i][0], yi = points[i][1];
int xj = points[j][0], yj = points[j][1];
edges.push_back({i, j, abs(xi - xj) + abs(yi - yj)});
}
}
// 根据权重进行排序
sort(edges.begin(), edges.end(),
[](const auto& a, const auto& b) { return a[2] < b[2]; });
// 返回最终结果
int mst = 0;
UF uf(n);
for (int i = 0; i < edges.size(); ++i) {
int u = edges[i][0];
int v = edges[i][1];
int w = edges[i][2];
// 如果uv之前已经连通,再连接会成环,所以跳过这次连接
if (uf.isConnected(u, v)) {
continue;
}
mst += w;
// 连接uv
uf._union(u, v);
}
return mst;
}
};