LeetCode 1584中等高级图
连接所有点的最小费用 图解题解
这道题到底在问什么
给一组点 points[i]=[xi,yi]。连接两点 i、j 的成本为它们的曼哈顿距离 |xi-xj|+|yi-yj|。求让所有点互相连通的最小总成本。
- 输入
- points=[[0,0],[2,2],[3,10],[5,2],[7,0]]
- 输出
- 20
最优解:一步一步想明白
- 3记住这一句,下面每条边都在套它。
- 4起手:5 个点各自孤立,一条边都还没选(费用 0)。下面把「所有点对」按曼哈顿距离从小到大排好,逐条尝试连接。
- 5看边 P1-P3:曼哈顿距离 = |2-5| + |2-2| = 3。这是当前最短的可用边,查两端的根——
- 6根不同、不成环 → 选入这条边!两组并成一组(同色),累计费用 +3 = 3。已选 1/4 条。
- 7看边 P0-P1:曼哈顿距离 = |0-2| + |0-2| = 4。这是当前最短的可用边,查两端的根——
- 8根不同、不成环 → 选入这条边!两组并成一组(同色),累计费用 +4 = 7。已选 2/4 条。
- 9看边 P3-P4:曼哈顿距离 = |5-7| + |2-0| = 4。这是当前最短的可用边,查两端的根——
- 10根不同、不成环 → 选入这条边!两组并成一组(同色),累计费用 +4 = 11。已选 3/4 条。
- 11看边 P0-P3:曼哈顿距离 = |0-5| + |0-2| = 7。先查根——
- 12P0、P3 已经在同一组里,再连会成环 → 跳过这条边,费用不变(仍是 11)。
- 13看边 P0-P4:曼哈顿距离 = |0-7| + |0-0| = 7。先查根——
- 14P0、P4 已经在同一组里,再连会成环 → 跳过这条边,费用不变(仍是 11)。
- 15看边 P1-P4:曼哈顿距离 = |2-7| + |2-0| = 7。先查根——
- 16P1、P4 已经在同一组里,再连会成环 → 跳过这条边,费用不变(仍是 11)。
- 17看边 P1-P2:曼哈顿距离 = |2-3| + |2-10| = 9。这是当前最短的可用边,查两端的根——
- 18根不同、不成环 → 选入这条边!两组并成一组(同色),累计费用 +9 = 20。已选 4/4 条。
- 19看边 P2-P3:曼哈顿距离 = |3-5| + |10-2| = 10。先查根——
- 20P2、P3 已经在同一组里,再连会成环 → 跳过这条边,费用不变(仍是 20)。
- 21看边 P0-P2:曼哈顿距离 = |0-3| + |0-10| = 13。先查根——
- 22P0、P2 已经在同一组里,再连会成环 → 跳过这条边,费用不变(仍是 20)。
- 23看边 P2-P4:曼哈顿距离 = |3-7| + |10-0| = 14。先查根——
- 24P2、P4 已经在同一组里,再连会成环 → 跳过这条边,费用不变(仍是 20)。
- 25选满 4 条边,所有点连成一棵树(同一组同色)。最小总费用 = 20。这就是答案。
⚠️ 容易写错的地方
✗ 错:漏了「不成环」判断
✓ 对:选边前必须 find 两端、根不同才选
否则会连出环、费用偏大且不是树
✗ 错:用欧氏距离
✓ 对:本题边权是曼哈顿距离 |Δx|+|Δy|
题目明确定义为曼哈顿距离
✗ 错:不知何时停
✓ 对:选满 n-1 条边即全连通,可提前 break
n 个点的生成树恰好 n-1 条边
完整代码(Java / Python / C++)
Java
import java.util.*;
class Solution {
int[] parent;
public int minCostConnectPoints(int[][] points) {
int n = points.length;
parent = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
List<int[]> edges = new ArrayList<>();
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++) {
int w = Math.abs(points[i][0] - points[j][0])
+ Math.abs(points[i][1] - points[j][1]);
edges.add(new int[]{w, i, j});
}
edges.sort((a, b) -> a[0] - b[0]);
int cost = 0, used = 0;
for (int[] e : edges) {
int ra = find(e[1]), rb = find(e[2]);
if (ra != rb) { parent[rb] = ra; cost += e[0]; used++; }
if (used == n - 1) break;
}
return cost;
}
int find(int x) {
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
return x;
}
}Python
class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
n = len(points)
parent = list(range(n))
def find(x):
while parent[x] != x:
parent[x] = parent[parent[x]]
x = parent[x]
return x
edges = []
for i in range(n):
for j in range(i + 1, n):
w = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1])
edges.append((w, i, j))
edges.sort()
cost = used = 0
for w, i, j in edges:
ri, rj = find(i), find(j)
if ri != rj:
parent[rj] = ri
cost += w
used += 1
if used == n - 1:
break
return costC++
class Solution {
public:
vector<int> parent;
int find(int x) {
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
return x;
}
int minCostConnectPoints(vector<vector<int>>& points) {
int n = points.size();
parent.resize(n);
for (int i = 0; i < n; i++) parent[i] = i;
vector<array<int,3>> edges;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++) {
int w = abs(points[i][0] - points[j][0])
+ abs(points[i][1] - points[j][1]);
edges.push_back({w, i, j});
}
sort(edges.begin(), edges.end());
int cost = 0, used = 0;
for (auto& e : edges) {
int ra = find(e[1]), rb = find(e[2]);
if (ra != rb) { parent[rb] = ra; cost += e[0]; used++; }
if (used == n - 1) break;
}
return cost;
}
};复杂度
时间
O(n²·log n)
n² 条边排序占主项,find 近乎常数
空间
O(n²)
要存所有点对的边
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 连接所有点的最小费用 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么按边权从小到大就能得到最小生成树?+
贪心:每次在不成环的前提下选最便宜的边一定安全(割性质保证它属于某棵 MST),逐步扩张到全连通即最优。
Kruskal 和 Prim 怎么选?+
稠密图(本题点对全连边)Prim + 堆更省;稀疏图 Kruskal 更直观。本题 n 小,两者都行,Kruskal 配并查集思路最清晰。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 连接所有点的最小费用 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。