leetcode
leetcode 701 ~ 750
公交路线

公交路线

难度:

标签:

题目描述

代码结果

运行时间: 116 ms, 内存: 40.6 MB


/*
 * 思路:
 * 1. 使用广度优先搜索(BFS)来寻找最短路径。
 * 2. 创建一个映射,将每个车站映射到经过该车站的公交车集合。
 * 3. 使用队列进行BFS,记录访问过的车站和公交车。
 * 4. 每次从队列中取出一个车站,遍历经过该车站的所有公交车。
 * 5. 对于每辆公交车,遍历其所有经过的车站,如果是目标车站,返回当前乘坐公交车的数量。
 * 6. 如果所有可能性都遍历完还没找到目标车站,返回-1。
 */
import java.util.*;
import java.util.stream.Collectors;

public class Solution {
    public int numBusesToDestination(int[][] routes, int source, int target) {
        if (source == target) return 0;
        Map<Integer, Set<Integer>> stationToBuses = Arrays.stream(routes)
            .flatMapToInt(Arrays::stream)
            .boxed()
            .distinct()
            .collect(Collectors.toMap(
                station -> station,
                station -> new HashSet<>(),
                (a, b) -> a,
                HashMap::new
            ));
        for (int bus = 0; bus < routes.length; bus++) {
            for (int station : routes[bus]) {
                stationToBuses.get(station).add(bus);
            }
        }
        Queue<int[]> queue = new LinkedList<>();
        Set<Integer> visitedStations = new HashSet<>();
        Set<Integer> visitedBuses = new HashSet<>();
        queue.offer(new int[]{source, 0});
        visitedStations.add(source);
        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int station = current[0];
            int buses = current[1];
            for (int bus : stationToBuses.getOrDefault(station, new HashSet<>())) {
                if (visitedBuses.contains(bus)) continue;
                visitedBuses.add(bus);
                for (int nextStation : routes[bus]) {
                    if (nextStation == target) return buses + 1;
                    if (visitedStations.contains(nextStation)) continue;
                    visitedStations.add(nextStation);
                    queue.offer(new int[]{nextStation, buses + 1});
                }
            }
        }
        return -1;
    }
}

解释

方法:

该题解采用了宽度优先搜索(BFS)的策略。首先,使用一个字典 transpose 将站点和公交路线进行映射,键为站点,值为经过该站点的所有公交路线的索引。接着,从源站点出发,使用队列进行层序遍历,每一层代表乘坐一次公交车。遍历过程中,记录已访问的站点和已乘坐的公交路线,以避免重复访问。当到达目标站点时,返回当前的乘坐次数。

时间复杂度:

O(S * R)

空间复杂度:

O(S * R)

代码细节讲解

🦆
在算法中,为什么选择宽度优先搜索(BFS)而不是深度优先搜索(DFS)来解决这个问题?
宽度优先搜索(BFS)是解决此类问题的理想选择,因为它可以保证在找到目标站点时已经使用的公交车次数是最少的。BFS通过逐层探索所有可能的路径直到找到目标,这对于最短路径问题非常适合。而深度优先搜索(DFS)可能会深入探索某一条路径而忽略了更短的替代路径,因此它不适合用于寻找最少次数的问题。
🦆
构建`transpose`字典时,为什么需要将站点作为键,而公交路线索引作为值?这样的数据结构设计有什么特别的好处?
使用站点作为键,公交路线索引作为值的方式构建`transpose`字典有利于快速查找任何站点可直接乘坐的所有公交路线。这种映射结构使得在宽度优先搜索过程中可以迅速确定从当前站点可以直接到达的其他站点,从而有效地遍历和转换乘车线路。此外,这种结构还有助于避免重复检查已乘坐的公交线路,因为我们可以直接通过站点查询相关的公交路线,从而优化搜索效率。
🦆
在BFS中使用队列时,每次从队列中取出元素时都进行了什么操作?这对找到最少乘坐次数的目标有何影响?
在BFS中,每次从队列中取出一个元素(即当前公交路线以及已乘坐的次数),我们会检查通过当前路线可以到达的所有站点。如果其中包含目标站点,则直接返回当前乘坐次数加一。如果不包含目标站点,则将从当前站点可达的、尚未访问过的站点加入队列,同时更新已访问站点和已乘坐路线的记录。这种方法确保每次扩展都是在尝试最少的乘坐次数的情况下进行,因此可以保证找到的第一个符合条件的结果就是乘坐次数最少的解。
🦆
算法中如何处理循环路线,即公交车线路中包含重复站点的情况?
在本算法实现中,通过维护一个已访问站点集合`visited`来处理循环路线。每次访问一个站点前,会检查该站点是否已经被访问过。如果已经访问过,则不会再次对该站点进行操作,从而避免了进入循环路线导致的无限循环问题。这种方法有效地避免了重复访问同一站点,从而处理了可能的循环路线问题。

相关问题