1. 题目背景与核心需求
这道题目来自阿里巴巴2026年春招算法岗的第三题,考察的是铁路修建的最优路径规划问题。题目场景设定为:给定一个由多个城市组成的网络,城市之间可以通过修建铁路相连,每条铁路有对应的修建成本。要求设计一个算法,找到连接所有城市的最低成本铁路修建方案。
这类问题在实际工程中非常常见,比如:
- 城市间光纤网络铺设
- 电力传输网络规划
- 物流配送中心选址与路线设计
2. 问题建模与算法选择
2.1 问题抽象化
我们可以将这个问题抽象为图论中的最小生成树(MST)问题:
- 城市 = 图的顶点
- 可能的铁路 = 图的边
- 修建成本 = 边的权重
2.2 算法对比分析
解决MST问题的经典算法主要有两种:
| 算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| Kruskal | O(E log E) | O(E) | 稀疏图(边少) |
| Prim | O(E log V) | O(V) | 稠密图(边多) |
考虑到铁路修建问题通常是稀疏图(城市数量多但直接连接少),Kruskal算法更为合适。
3. Kruskal算法实现详解
3.1 核心数据结构
实现Kruskal算法需要两个关键组件:
- 并查集(Union-Find):用于高效判断是否形成环
- 优先队列:按权重排序所有边
3.2 Java实现代码
java复制import java.util.*;
class Solution {
class UnionFind {
int[] parent;
int[] rank;
public UnionFind(int size) {
parent = new int[size];
rank = new int[size];
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public boolean union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) return false;
if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
return true;
}
}
public int minCostConnectCities(int n, int[][] connections) {
// 按成本升序排序所有连接
Arrays.sort(connections, (a, b) -> a[2] - b[2]);
UnionFind uf = new UnionFind(n);
int totalCost = 0;
int edgesUsed = 0;
for (int[] conn : connections) {
int city1 = conn[0];
int city2 = conn[1];
int cost = conn[2];
if (uf.union(city1, city2)) {
totalCost += cost;
edgesUsed++;
if (edgesUsed == n - 1) break;
}
}
return edgesUsed == n - 1 ? totalCost : -1;
}
}
3.3 C++实现代码
cpp复制#include <vector>
#include <algorithm>
using namespace std;
class UnionFind {
private:
vector<int> parent;
vector<int> rank;
public:
UnionFind(int size) {
parent.resize(size);
rank.resize(size, 0);
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
bool unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) return false;
if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
return true;
}
};
class Solution {
public:
int minCostConnectCities(int n, vector<vector<int>>& connections) {
// 按成本升序排序所有连接
sort(connections.begin(), connections.end(),
[](const vector<int>& a, const vector<int>& b) {
return a[2] < b[2];
});
UnionFind uf(n);
int totalCost = 0;
int edgesUsed = 0;
for (const auto& conn : connections) {
int city1 = conn[0];
int city2 = conn[1];
int cost = conn[2];
if (uf.unite(city1, city2)) {
totalCost += cost;
edgesUsed++;
if (edgesUsed == n - 1) break;
}
}
return edgesUsed == n - 1 ? totalCost : -1;
}
};
3.4 Python实现代码
python复制class UnionFind:
def __init__(self, size):
self.parent = list(range(size))
self.rank = [0] * size
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
rootX = self.find(x)
rootY = self.find(y)
if rootX == rootY:
return False
if self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
elif self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
else:
self.parent[rootY] = rootX
self.rank[rootX] += 1
return True
class Solution:
def minCostConnectCities(self, n: int, connections: List[List[int]]) -> int:
connections.sort(key=lambda x: x[2])
uf = UnionFind(n)
total_cost = 0
edges_used = 0
for city1, city2, cost in connections:
if uf.union(city1, city2):
total_cost += cost
edges_used += 1
if edges_used == n - 1:
break
return total_cost if edges_used == n - 1 else -1
4. 算法优化与边界处理
4.1 输入验证
在实际工程中,我们需要考虑以下边界情况:
- 城市数量n <= 1时直接返回0
- 连接数量不足n-1时直接返回-1
- 连接中存在无效城市编号时的处理
4.2 性能优化
对于大规模数据,可以考虑以下优化:
- 使用更高效的排序算法(如计数排序)当成本范围有限时
- 并查集的路径压缩和按秩合并优化(已实现)
- 提前终止条件(已实现)
4.3 测试用例设计
完整的测试应该包括:
- 基本连通情况
- 完全图情况
- 存在多个相同成本边的情况
- 不连通图的情况
- 大规模数据测试
5. 实际工程应用扩展
5.1 动态铁路网络维护
在实际系统中,铁路网络可能会动态变化:
- 新增城市
- 现有铁路损坏
- 修建成本变化
可以扩展算法支持增量更新,使用动态MST算法。
5.2 多目标优化
真实场景可能需要考虑:
- 建设时间约束
- 环境影响评估
- 政治经济因素
这需要将单目标MST扩展为多目标优化问题。
5.3 分布式计算实现
对于全国范围的铁路规划,数据量可能非常大,可以考虑:
- 分区域计算后合并
- 使用MapReduce等分布式框架
- 图划分技术
6. 面试技巧与准备建议
6.1 常见面试问题
- 为什么选择Kruskal而不是Prim算法?
- 并查集的时间复杂度是多少?
- 如何处理修建成本相同的情况?
- 如果某些城市之间不能直接连接怎么办?
6.2 算法岗准备路线
- 基础数据结构:数组、链表、树、图
- 经典算法:排序、搜索、动态规划、贪心
- 系统设计:分布式算法、大数据处理
- 编码实践:LeetCode、Codeforces刷题
6.3 代码规范建议
- 阿里巴巴Java开发手册要求
- 清晰的变量命名
- 适当的注释
- 异常处理
- 单元测试
7. 在线测试注意事项
- 理解题目输入输出格式
- 处理边界条件
- 测试代码运行时间
- 检查内存使用
- 验证极端情况
在实际编写代码时,我通常会先写几个简单的测试用例手动验证,然后再处理更复杂的情况。对于这类图算法问题,绘制简单的示例图有助于理解算法执行过程。
