华为 OD 训练营 · 题解精讲
LeetCode1334. 阈值距离内邻居最少的城市
题目描述
有 n 个城市,按从 0 到 n-1 编号。给你一个边数组 edges,其中 edges[i] = [from(i), to(i), weight(i)] 代表 from(i) 和 to(i)两个城市之间的双向加权边,距离阈值是一个整数 distanceThreshold。 返回在路径距离限制为 distanceThreshold 以内可到达城市最少的城市。如果有多个这样的城市,则返回编号最大的城市。 注意,连接城市 i 和 j 的路径的距离等于沿该路径的所有边的权重之和。
示例 1: CFzrbRBQ0o0gfIxDMgDctGlsne6.png
输入: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: EuShbeSdlo5SKXxV5gTcXxdanUe.png
输入:n = 5, edges = [[0,1,2],[0,4,8],[1,2,3],[1,4,2],[2,3,1],[3,4,1]], distanceThreshold = 2 输出:0 解释:城市分布图如上。 每个城市阈值距离 distanceThreshold = 2 内的邻居城市分别是: 城市 0 -> [城市 1] 城市 1 -> [城市 0, 城市 4] 城市 2 -> [城市 3, 城市 4] 城市 3 -> [城市 2, 城市 4] 城市 4 -> [城市 1, 城市 2, 城市 3] 城市 0 在阈值距离 2 以内只有 1 个邻居城市。
提示: 2 <= n <= 100 1 <= edges.length <= n * (n - 1) / 2 edges[i].length == 3 0 <= from(i) < to(i) < n 1 <= weight(i), distanceThreshold <= 10^4 所有 (from(i), to(i)) 都是不同的。
题目解析
题目在说什么
想象你是一个城市规划师,有 n 个城市,编号从 0 到 n-1。城市之间有道路连接,每条路有长度(权重)。现在给你一个“步行距离限制” distanceThreshold,比如 4 公里。你要找出一个城市,从它出发,能在 4 公里内到达的其他城市数量最少。如果多个城市满足条件,就选编号最大的那个。
比如示例 1 中,城市 0 能在 4 公里内到达城市 1 和 2(共 2 个),城市 3 也能到达城市 1 和 2(也是 2 个),但城市 3 编号更大,所以答案是 3。
思路是怎么来的
这个问题本质上是要计算“每个城市到其他所有城市的最短距离”,然后数一数每个城市有多少个“邻居”(距离 ≤ distanceThreshold 的其它城市)。最后比较哪个城市的邻居最少。
为什么需要计算“所有城市到所有城市”的最短距离?因为题目问的是“每个城市”的邻居数量,而不是只问某个特定城市。所以我们需要一个能一次性算出所有城市之间最短路径的算法——这就是 Floyd 算法。
Floyd 算法的核心思想很生活化:假设你要从城市 A 到城市 B,你可以直接走,也可以绕道经过城市 C。如果绕道更近,那就选绕道。Floyd 算法就是让每个城市都当一次“中间人”,看看经过它能不能让其他城市之间的路更短。
代码逐步拆解
第一步:初始化距离矩阵
int[][] grid = new int[n][n];
for (int[] row : grid) {
Arrays.fill(row, 1000000); // 先填一个大数,表示“还没通”
}
for (int[] edge : edges) {
int a = edge[0], b = edge[1], w = edge[2];
grid[a][b] = w;
grid[b][a] = w; // 双向道路,所以两个方向都填
}
for (int i = 0; i < n; i++){
grid[i][i] = 0; // 自己到自己的距离是0
}这里我们建了一个 n×n 的表格,grid[i][j] 表示城市 i 到城市 j 的最短距离。一开始,除了直接相连的城市,其他都设为很大的数(1000000 足够大,因为题目中距离最大是 10000)。注意双向道路要填两次。
第二步:Floyd 核心——让每个城市当“中间人”
for (int mid = 0; mid < n; mid++) { // 中间城市
for (int start = 0; start < n; start++) { // 起点
for (int end = 0; end < n; end++) { // 终点
if (grid[start][end] > grid[start][mid] + grid[mid][end]) {
grid[start][end] = grid[start][mid] + grid[mid][end];
grid[end][start] = grid[start][mid] + grid[mid][end]; // 对称更新
}
}
}
}三层循环:最外层是“中间城市” mid,内层是起点 start 和终点 end。对于每一对 (start, end),我们检查:直接走更近,还是绕道经过 mid 更近?如果绕道更近,就更新距离。