⭐算法OJ⭐连接所有点的最小费用【最小生成树】(C++实现)Min Cost to Connect All Points
文章目录
- 问题描述
- 解题思路
- 方法1:Kruskal算法(推荐)
- 代码实现(Kruskal + Union-Find)
- 复杂度分析
- 方法2:Prim算法
- 代码实现(Prim算法)
- 复杂度分析
1584. Min Cost to Connect All Points
You are given an array points representing integer coordinates of some points on a 2D-plane, where
points[i] = [xi, yi].
The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val.
Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points.
问题描述
给定一组二维平面上的点,其中 points[i] = [xi, yi]。连接两点 [xi, yi] 和 [xj, yj] 的费用是它们之间的曼哈顿距离:|xi - xj| + |yi - yj|。返回连接所有点所需的最小总费用。所有点都应该被连接,并且任意两点之间有且只有一条路径。
Example 1:

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
Output: 20
Explanation:
We can connect the points as shown above to get the minimum cost of 20.
Notice that there is a unique path between every pair of points.

Example 2:
Input: points = [[3,12],[-2,5],[-4,1]]
Output: 18
解题思路
这是一个典型的 最小生成树(MST) 问题,可以使用 Kruskal算法 或 Prim算法 解决。
方法1:Kruskal算法(推荐)
- 计算所有边:生成所有可能的点对,并计算它们的曼哈顿距离。
- 按权重排序边:从小到大排序所有边。
- 使用并查集(Union-Find):
- 初始化每个点为一个独立的集合。
- 遍历排序后的边,如果两个点不在同一集合,就合并它们,并累加费用。
- 直到所有点连通(即边的数量 = n-1,其中 n 是点的数量)。
代码实现(Kruskal + Union-Find)
#include <vector>
#include <algorithm>
using namespace std;class UnionFind {
private:vector<int> parent;vector<int> rank;
public:UnionFind(int n) {parent.resize(n);rank.resize(n, 0);for (int i = 0; i < n; i++) {parent[i] = i;}}int find(int u) {if (parent[u] != u) {parent[u] = find(parent[u]); // Path compression}return parent[u];}bool unionSets(int u, int v) {int rootU = find(u);int rootV = find(v);if (rootU == rootV) return false; // Already connectedif (rank[rootU] > rank[rootV]) {parent[rootV] = rootU;} else if (rank[rootU] < rank[rootV]) {parent[rootU] = rootV;} else {parent[rootV] = rootU;rank[rootU]++;}return true;}
};class Solution {
public:int minCostConnectPoints(vector<vector<int>>& points) {int n = points.size();vector<vector<int>> edges; // {cost, u, v}// Generate all possible edgesfor (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {int cost = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);edges.push_back({cost, i, j});}}// Sort edges by costsort(edges.begin(), edges.end());UnionFind uf(n);int res = 0;int edgesUsed = 0;for (auto& edge : edges) {int cost = edge[0], u = edge[1], v = edge[2];if (uf.unionSets(u, v)) {res += cost;edgesUsed++;if (edgesUsed == n - 1) break;}}return res;}
};
复杂度分析
- 时间复杂度: O ( n ² l o g n ) O(n^² log n) O(n²logn)(排序边)。
- 空间复杂度: O ( n ² ) O(n^²) O(n²)(存储所有边)。
方法2:Prim算法
- 初始化优先队列(最小堆):从任意一个点开始,将其所有邻边加入堆。
- 逐步扩展MST:
- 每次取出权重最小的边,如果该边连接的点未被访问,则加入MST,并累加费用。
- 将该点的所有邻边加入堆。
- 直到所有点都被访问。
代码实现(Prim算法)
#include <vector>
#include <queue>
#include <climits>
using namespace std;class Solution {
public:int minCostConnectPoints(vector<vector<int>>& points) {int n = points.size();vector<bool> visited(n, false);priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;// Start with node 0minHeap.push({0, 0});int res = 0;int edgesUsed = 0;while (edgesUsed < n) {auto [cost, u] = minHeap.top();minHeap.pop();if (visited[u]) continue;visited[u] = true;res += cost;edgesUsed++;// Add all edges from u to unvisited nodesfor (int v = 0; v < n; v++) {if (!visited[v]) {int newCost = abs(points[u][0] - points[v][0]) + abs(points[u][1] - points[v][1]);minHeap.push({newCost, v});}}}return res;}
};
复杂度分析
- 时间复杂度: O ( n ² l o g n ) O(n^² log n) O(n²logn)(优先队列操作)。
- 空间复杂度: O ( n ) O(n) O(n)(存储 visited 和优先队列)。
