LeetCode 815公交路线题:我用BFS建了个‘车站-线路’哈希表,换乘次数最少就这么算
LeetCode 815公交路线题从现实网络到图论模型的思维跃迁第一次看到LeetCode 815公交路线题时我盯着题目描述足足五分钟——不是因为题目有多难而是被它巧妙的现实映射所吸引。这道题把城市公交系统抽象为一个图论问题要求我们计算从起点到终点的最少换乘次数。表面上看是道普通的BFS题但当你真正开始建模时会发现传统的站到站建图方式在这里完全行不通。本文将带你经历一次完整的算法思维训练如何把现实世界的复杂系统转化为计算机可以高效处理的图结构。1. 为什么传统建图方式在这里失效大多数图论问题中我们会很自然地把实体作为节点关系作为边。比如在社交网络分析中把人作为节点好友关系作为边在地图导航中把路口作为节点道路作为边。按照这种直觉公交路线题似乎应该把每个车站作为节点如果两站属于同一条线路就连一条边。但实际操作中这种建模方式会带来两个致命问题空间复杂度爆炸假设有N条线路每条线路平均包含M个车站那么完全连接的边数将达到O(N*M²)。对于题目给出的上限线路500条总车站数10^5这种建图方式根本无法承受。信息丢失更重要的是这种建模丢失了公交线路这一关键实体。当我们说换乘时实际是在不同线路间切换而不是简单地从A站到B站。忽略线路实体会导致无法准确统计换乘次数。# 传统建图方式的伪代码不适用于本题 graph defaultdict(set) for route in routes: for i in range(len(route)): for j in range(i1, len(route)): graph[route[i]].add(route[j]) graph[route[j]].add(route[i])2. 站-线-站二级跳转模型的突破解决这个问题的关键洞察在于我们需要同时考虑车站和线路两种实体。这引出了站-线-站的二级跳转模型第一级映射建立车站→所属线路的哈希表第二级跳转通过线路找到可达的新车站这种建模方式的优势在于空间效率只需存储每个车站对应的线路列表空间复杂度为O(S)其中S是总车站数逻辑清晰准确反映了现实中的换乘行为——先到达车站再选择新线路// 关键数据结构车站到线路的映射 HashMapInteger, ListInteger stationToRoutes new HashMap(); for (int i 0; i routes.length; i) { for (int station : routes[i]) { stationToRoutes.computeIfAbsent(station, k - new ArrayList()).add(i); } }3. 基于线路的BFS换乘次数的精准统计有了上述数据结构后BFS的过程就变得直观了初始化找到起点站所在的所有线路加入队列层级遍历每次处理一层的所有线路通过车站找到下一层可到达的新线路终止条件当发现目标车站出现在当前线路中时立即返回这种设计确保了换乘次数统计的准确性每深入一层BFS相当于一次换乘由于BFS的特性首次到达目标时的层数必然是最小的注意题目要求返回的是乘坐公交车的总数所以最终结果是换乘次数1初始乘坐也算一次4. 优化技巧与边界处理在实际编码中有几个关键点需要特别注意预处理检查如果起点和终点相同直接返回0线路去重使用boolean数组标记已访问线路避免重复处理层级控制使用两个列表交替处理当前层和下一层清晰跟踪换乘次数def numBusesToDestination(routes, source, target): if source target: return 0 # 建表车站→[线路] station_to_routes defaultdict(list) for i, route in enumerate(routes): for station in route: station_to_routes[station].append(i) # BFS初始化 visited [False] * len(routes) queue deque() for route_idx in station_to_routes[source]: queue.append(route_idx) visited[route_idx] True transfers 0 while queue: next_level [] for _ in range(len(queue)): current_route queue.popleft() # 检查当前线路是否包含目标车站 if target in routes[current_route]: return transfers 1 # 通过当前线路的车站找到下一层线路 for station in routes[current_route]: for neighbor_route in station_to_routes[station]: if not visited[neighbor_route]: visited[neighbor_route] True next_level.append(neighbor_route) queue deque(next_level) transfers 1 return -15. 从算法到现实建模思维的通用性这道题的精妙之处不仅在于算法本身更在于它展示了一种通用的问题解决框架实体识别明确系统中的关键实体车站、线路关系建模确定实体间的交互方式车站属于哪些线路抽象转化将现实行为映射为计算机操作换乘→BFS层级推进这种思维可以扩展到许多相似场景航班中转机场作为车站航空公司航线作为线路社交网络用户作为车站群组/社区作为线路物流配送仓库作为车站运输路线作为线路在最近的一次技术面试中我遇到了一个分布式系统中的节点通信问题。灵光一现之下我直接套用了这道题的建模思路——将服务器节点视为车站通信协议视为线路完美解决了最优路径计算的问题。面试官对这样巧妙的类比赞不绝口这也让我更加确信真正有价值的不是记住算法而是培养这种将现实抽象为计算模型的能力。