⭐算法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 和优先队列)。