问题描述
小M有n个点,每个点的坐标为(xi,yi)(xi,yi)。她可以从一个点出发,平行于坐标轴移动,直到到达另一个点。具体来说,她可以从 (x1,y1)(x1,y1) 直接到达 (x2,y1)(x2,y1) 或者 (x1,y2)(x1,y2),但无法直接到达 (x2,y2)(x2,y2)。为了使得任意两个点之间可以互相到达,小M可以选择增加若干个新的点。
你的任务是计算最少需要增加多少个点,才能保证任意两个点之间可以通过平行于坐标轴的路径互相到达。
问题转化
给定 n 个点,确定最少需要增加多少个点,才能保证任意两点之间可以通过平行于坐标轴的路径互相到达。
思路
- 使用并查集(Union-Find)对点进行归类,判断哪些点在同一个连通块中。
- 横纵坐标相同的点可以直接归类到同一连通块。
- 每个独立的连通块需要至少一个新增点与其它连通块连接。
- 最终新增点数目 = 连通块数目 - 1。
代码实现
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;
class UnionSet {
private:
vector<int> parent;
vector<int> rank;
public:
UnionSet(int n) {
parent.resize(n);
rank.resize(n, 0);
for (int i = 0; i < n; ++i) {
parent[i] = i;
}
}
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]); // 路径压缩
}
return parent[x];
}
void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
};
int solution(int n, vector<vector<int>> points) {
unordered_map<int, vector<int>> xMap, yMap;
for (int i = 0; i < n; ++i) {
xMap[points[i][0]].push_back(i); // 按横坐标分组
yMap[points[i][1]].push_back(i); // 按纵坐标分组
}
UnionSet uf(n);
// 按横坐标连通
for (const auto& [x, indices] : xMap) {
for (int i = 1; i < indices.size(); ++i) {
uf.unite(indices[0], indices[i]);
}
}
// 按纵坐标连通
for (const auto& [y, indices] : yMap) {
for (int i = 1; i < indices.size(); ++i) {
uf.unite(indices[0], indices[i]);
}
}
// 统计连通块数量
unordered_set<int> uniqueRoots;
for (int i = 0; i < n; ++i) {
uniqueRoots.insert(uf.find(i));
}
return uniqueRoots.size() - 1; // 最少需要新增的点数
}
int main() {
cout << (solution(2, {{1, 1}, {2, 2}}) == 1) << endl;
cout << (solution(3, {{1, 2}, {2, 3}, {4, 1}}) == 2) << endl;
cout << (solution(4, {{3, 4}, {5, 6}, {3, 6}, {5, 4}}) == 0) << endl;
return 0;
}
算法分析
-
分组操作:O(n)
- 遍历所有点,将点分组到
xMap和yMap。
- 遍历所有点,将点分组到
-
并查集操作:近似 O(n)
- 连通操作和查询操作的时间复杂度接近 O(1),总共执行 2n 次。
-
统计连通块:O(n)
- 遍历所有点,统计连通块根节点。
综上所述:总复杂度:O(n)