在问 · 算法题解

815. 公交路线

题目描述

输入数组 routes,routes[i] 是第 i 辆公交车经过的车站编号序列。每辆车沿着自己的序列不停循环行驶,例如 [1,5,7] 表示按 1→5→7→1→5→7→… 行驶。

你从车站 source 出发,一开始不在任何车上,目标是到达车站 target。移动只能靠乘坐公交车,可以在两条线路共有的车站换乘。

返回最少需要乘坐多少辆公交车。如果无法到达,返回 -1。若 source 与 target 相同,则不需要乘车。

示例 1
输入:routes = [[1,2,7],[3,6,7]], source = 1, target = 6
输出:2

解释:先乘第 0 辆车到车站 7,再换乘第 1 辆车到车站 6,共 2 辆。

示例 2
输入:routes = [[7,12],[4,5,15],[6],[15,19],[9,12,13]], source = 15, target = 12
输出:-1

解释:从车站 15 能到达的线路都无法连到车站 12。

数据范围

  • 1 <= routes.length <= 500
  • 1 <= routes[i].length <= 10^5
  • routes[i] 中的值各不相同。
  • sum(routes[i].length) <= 10^5
  • 0 <= routes[i][j] < 10^6
  • 0 <= source, target < 10^6

解题思路

代价按“乘坐的公交车数”计算,而不是车站数。先建立“车站 → 经过它的公交车”的索引。以 source 为第 0 层做 BFS,每一层代表多坐一辆车:从当前层的每个车站出发,找到所有还没坐过的公交车,把这些车经过的所有车站一次性加入下一层。每辆公交车只展开一次,第一次碰到 target 时的层数就是答案。

为什么正确

第 rides 层展开的车站,恰好是最少乘坐 rides 辆车才能到达的车站。

复杂度分析

时间 O(Σ|routes[i]|):每辆车只展开一次,每个车站只入队一次。空间 O(Σ|routes[i]|) 用于索引。

易错点

source == target 时不需要坐车,答案是 0。另外要标记已坐过的公交车,否则一辆很长的线路会被反复展开导致超时。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def numBusesToDestination(self, routes: List[List[int]], source: int, target: int) -> int:
        if source == target:
            return 0
        stop_to_buses = collections.defaultdict(list)
        for bus, route in enumerate(routes):
            for stop in route:
                stop_to_buses[stop].append(bus)
        used_bus = set()
        seen_stop = {source}
        frontier = [source]
        rides = 0
        while frontier:
            rides += 1
            nxt = []
            for stop in frontier:
                for bus in stop_to_buses[stop]:
                    if bus in used_bus:
                        continue
                    used_bus.add(bus)
                    for s in routes[bus]:
                        if s == target:
                            return rides
                        if s not in seen_stop:
                            seen_stop.add(s)
                            nxt.append(s)
            frontier = nxt
        return -1

载入编辑器,自己跑一遍 →

图论 专题的其他题目

查看 图论 全部题目 →