笔记-LeetCode 787: K 站中转内最便宜的航班

题目描述

有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。

现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst 的价格最便宜,并返回该价格。如果不存在这样的路线,则输出 -1。


代码模板(BFS + 最短路径搜索)

import java.util.*;

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        // 1. 建立邻接表 (有向图),存储每个城市的出发航班
        Map> graph = new HashMap<>();
        for (int[] flight : flights) {
            // flight[0] 是出发城市,flight[1] 是目的地,flight[2] 是票价
            graph.computeIfAbsent(flight[0], x -> new ArrayList<>()).add(new int[]{flight[1], flight[2]});
        }

        // 2. BFS 队列存储 [当前城市, 当前累计价格, 剩余中转次数]
        Queue queue = new LinkedList<>();
        queue.offer(new int[]{src, 0, k + 1});

        // 3. 记录到达每个城市的最小花费,初始化为最大值,防止不必要的重复遍历
        int[] minCost = new int[n];
        Arrays.fill(minCost, Integer.MAX_VALUE);
        minCost[src] = 0;

        // 4. 开始 BFS 遍历
        while (!queue.isEmpty()) {
            int[] node = queue.poll();
            int city = node[0]; // 当前所在城市
            int cost = node[1]; // 当前累计的票价
            int stops = node[2]; // 剩余可中转次数

            if (stops > 0) { // 只有还有中转次数,才能继续前进
                for (int[] next : graph.getOrDefault(city, new ArrayList<>())) {
                    int nextCity = next[0]; // 下一站城市
                    int price = next[1];    // 从当前城市到下一站的票价

                    // 只有找到更便宜的价格才更新,并且入队
                    if (cost + price < minCost[nextCity]) {
                        minCost[nextCity] = cost + price;
                        queue.offer(new int[]{nextCity, cost + price, stops - 1});
                    }
                }
            }
        }

        // 5. 返回最小花费,如果仍为 Integer.MAX_VALUE 说明无法到达
        return minCost[dst] == Integer.MAX_VALUE ? -1 : minCost[dst];
    }
}

代码详细解析

1. 建立邻接表

我们用 Map> graph 来存储航班信息,使得 graph.get(i) 代表城市 i 出发的所有航班列表。

示例输入:

flights = [
    [0, 1, 100],
    [1, 2, 100],
    [2, 0, 100],
    [1, 3, 600],
    [2, 3, 200]
]

存入 graph 后的结构:

{
    0 -> [[1, 100]],
    1 -> [[2, 100], [3, 600]],
    2 -> [[0, 100], [3, 200]]
}

2. 初始化 BFS 队列

我们使用 BFS 进行搜索,每个队列元素存储 [当前城市, 当前累计价格, 剩余中转次数]。 初始队列:

queue = [[src, 0, k + 1]]

3. 记录最小花费

minCost[i] 记录从 src 出发到 i 的最小花费,初始时设为 Integer.MAX_VALUE

Arrays.fill(minCost, Integer.MAX_VALUE);
minCost[src] = 0;

4. BFS 遍历

每次从队列取出一个城市 city,如果 stops > 0,则遍历 city 可达的所有 nextCity

如果 cost + price < minCost[nextCity],就更新 minCost[nextCity] 并入队。

示例流程(假设 k = 1):

1. 初始:queue = [[0, 0, 2]]
2. 取出 [0, 0, 2],发现 0 -> 1 (100)
   -> queue = [[1, 100, 1]]
3. 取出 [1, 100, 1],发现 1 -> 2 (100) 和 1 -> 3 (600)
   -> queue = [[2, 200, 0], [3, 700, 0]]
4. 取出 [2, 200, 0],但 stops = 0,不再继续。
5. 取出 [3, 700, 0],但 stops = 0,不再继续。

最终 minCost[dst] 存储了 src -> dst 的最小票价。


适用场景

这类题目通常具备以下特点:

  • 图结构:有向图或无向图,可能有权重。
  • 最短路径:求从起点到终点的最小花费/最短路径。
  • 有步数/层数限制
    • 不能走超过 K 步(例如中转站问题)。
    • 只能经过固定次数的边。

记忆关键点

  1. 邻接表存储图
Map> graph = new HashMap<>();
graph.put(from, [to, price])
  1. BFS 队列存储 {当前城市, 当前累积价格, 剩余中转次数}
Queue queue = new LinkedList<>();
queue.offer(new int[]{src, 0, k + 1});
  1. 最短路径的动态更新
if (cost + price < minCost[nextCity]) {
    minCost[nextCity] = cost + price;
    queue.offer(new int[]{nextCity, cost + price, stops - 1});
}
  1. BFS 控制最多 k+1 层
if (stops > 0) { // 只有还可以中转才继续 }

总结 ✔ 这题可以作为 BFS + 限制步数最短路径的模板。 ✔ 适用于“有限步数内的最短路径”问题。 ✔ 熟练掌握后,可以解决一类类似的题目!

你可能感兴趣的:(算法)