Medium
题目描述
有 n 个城市,按从 0 到 n-1 编号。给你一个边数组 edges,其中 edges[i] = [fromi, toi, weighti] 代表 fromi 和 toi 两个城市之间的双向加权边,距离阈值是 distanceThreshold。
返回能够通过某些路径到达其他城市数目最少、且路径距离不超过 distanceThreshold 的城市。如果有多个这样的城市,则返回编号最大的城市。
注意,连接城市 i 和 j 的路径的距离等于沿该路径的所有边的权重之和。
示例 1:
输入:n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4
输出:3
解释:城市分布如图所示。
每个城市阈值距离 distanceThreshold = 4 内的邻居城市分别是:
城市 0 -> [城市 1, 城市 2]
城市 1 -> [城市 0, 城市 2, 城市 3]
城市 2 -> [城市 0, 城市 1, 城市 3]
城市 3 -> [城市 1, 城市 2]
城市 0 和 3 在阈值距离 4 以内都有 2 个邻居城市,但是我们需要返回城市 3,因为它的编号最大。
示例 2:
输入:n = 5, edges = [[0,1,2],[0,4,8],[1,2,3],[1,4,2],[2,3,1],[3,4,1]], distanceThreshold = 2
输出:0
提示:
- 2 <= n <= 100
- 1 <= edges.length <= n * (n - 1) / 2
- edges[i].length == 3
- 0 <= fromi < toi < n
- 1 <= weighti, distanceThreshold <= 10^4
- 所有 (fromi, toi) 都是不同的。
解题思路
这道题需要找到能够到达其他城市数量最少的城市。核心思路是计算任意两点间的最短距离,然后统计每个城市在阈值距离内能到达的城市数量。
解法一:Floyd-Warshall算法(推荐) 使用Floyd-Warshall算法计算所有点对间的最短距离。该算法通过动态规划思想,逐步更新任意两点间的最短路径。时间复杂度O(n³),对于n≤100的约束条件非常适合。
解法二:多次Dijkstra算法 从每个城市出发运行Dijkstra算法,计算到其他所有城市的最短距离。由于边权非负,Dijkstra算法保证正确性。时间复杂度O(n²logn)。
算法步骤:
- 初始化距离矩阵,同城市距离为0,有边的城市距离为边权,其余为无穷大
- 使用Floyd-Warshall算法更新最短距离
- 统计每个城市在阈值距离内能到达的城市数量
- 找到邻居数量最少的城市,如果有多个则选择编号最大的
由于题目要求返回编号最大的城市(当邻居数量相同时),我们从后往前遍历,这样能自然地选择更大的编号。
代码实现
class Solution {
public:
int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
// 初始化距离矩阵
vector<vector<int>> dist(n, vector<int>(n, 1e9));
// 初始化自身距离为0
for (int i = 0; i < n; i++) {
dist[i][i] = 0;
}
// 添加边
for (auto& edge : edges) {
int u = edge[0], v = edge[1], w = edge[2];
dist[u][v] = dist[v][u] = w;
}
// Floyd-Warshall算法
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
int minCount = n;
int result = 0;
// 统计每个城市的邻居数量
for (int i = 0; i < n; i++) {
int count = 0;
for (int j = 0; j < n; j++) {
if (i != j && dist[i][j] <= distanceThreshold) {
count++;
}
}
// 选择邻居数量最少的城市,如果相同则选择编号更大的
if (count <= minCount) {
minCount = count;
result = i;
}
}
return result;
}
};
class Solution:
def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
# 初始化距离矩阵
dist = [[float('inf')] * n for _ in range(n)]
# 初始化自身距离为0
for i in range(n):
dist[i][i] = 0
# 添加边
for u, v, w in edges:
dist[u][v] = dist[v][u] = w
# Floyd-Warshall算法
for k in range(n):
for i in range(n):
for j in range(n):
if dist[i][k] + dist[k][j] < dist[i][j]:
dist[i][j] = dist[i][k] + dist[k][j]
min_count = n
result = 0
# 统计每个城市的邻居数量
for i in range(n):
count = sum(1 for j in range(n) if i != j and dist[i][j] <= distanceThreshold)
# 选择邻居数量最少的城市,如果相同则选择编号更大的
if count <= min_count:
min_count = count
result = i
return result
public class Solution {
public int FindTheCity(int n, int[][] edges, int distanceThreshold) {
// 初始化距离矩阵
int[,] dist = new int[n, n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dist[i, j] = i == j ? 0 : int.MaxValue;
}
}
// 添加边
foreach (var edge in edges) {
int u = edge[0], v = edge[1], w = edge[2];
dist[u, v] = dist[v, u] = w;
}
// Floyd-Warshall算法
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i, k] != int.MaxValue && dist[k, j] != int.MaxValue &&
dist[i, k] + dist[k, j] < dist[i, j]) {
dist[i, j] = dist[i, k] + dist[k, j];
}
}
}
}
int minCount = n;
int result = 0;
// 统计每个城市的邻居数量
for (int i = 0; i < n; i++) {
int count = 0;
for (int j = 0; j < n; j++) {
if (i != j && dist[i, j] <= distanceThreshold) {
count++;
}
}
// 选择邻居数量最少的城市,如果相同则选择编号更大的
if (count <= minCount) {
minCount = count;
result = i;
}
}
return result;
}
}
var findTheCity = function(n, edges, distanceThreshold) {
// 初始化距离矩阵
const dist = Array(n).fill(null).map(() => Array(n).fill(Infinity));
// 初始化自身距离为0
for (let i = 0; i < n; i++) {
dist[i][i] = 0;
}
// 添加边
for (const [u, v, w] of edges) {
dist[u][v] = dist[v][u] = w;
}
// Floyd-Warshall算法
for (let k = 0; k < n; k++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
let minCount = n;
let result = 0;
// 统计每个城市的邻居数量
for (let i = 0; i < n; i++) {
let count = 0;
for (let j = 0; j < n; j++) {
if (i !== j && dist[i][j] <= distanceThreshold) {
count++;
}
}
// 选择邻居数量最少的城市,如果相同则选择编号更大的
if (count <= minCount) {
minCount = count;
result = i;
}
}
return result;
};
复杂度分析
| 算法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| Floyd-Warshall | O(n³) | O(n²) |
| 多次Dijkstra | O(n²logn) | O(n²) |
推荐使用Floyd-Warshall算法,因为:
- 代码实现简洁
- 对于n≤100的约束,O(n³)时间复杂度完全可接受
- 能够一次性计算所有点对间的最短距离