如何用A*算法处理非全连通节点最短路径?含公交换乘优化需求
解决非连通公交网下的A*路径规划(优先减少换乘)
这问题挺典型的,我之前做公交路径规划的时候也遇到过类似场景,给你捋捋具体怎么搞,完全适配你的BusRoute和NODE类结构:
1. 先重构你的NODE状态类
单纯记录当前站点远远不够——要追踪换乘次数,必须把当前所在线路加入状态。所以你的NODE应该包含这些字段:
- 当前站点(名称、纬度、经度)
- 当前所在线路的标识(比如给
BusRoute加个routeId字段,用字符串/数字都行) - 累计实际代价
g(n) - 累计换乘次数
- 父节点(用来回溯最终路径)
这样才能区分“在站点X坐1路”和“在站点X坐2路”这两种完全不同的状态,避免算法走回头路或者漏掉换乘机会。
2. 设计A*的代价函数(核心是换乘惩罚)
要让算法优先选择换乘少的路径,关键是给换乘操作设置远大于站点间距离的惩罚成本:
实际代价g(n)
g(n) = 从起点到当前节点的累计距离成本 + 累计换乘惩罚成本
- 站点间距离成本:用两个站点的经纬度算曼哈顿距离或者欧氏距离都行,比如:
def calculate_distance(station1, station2): # 简化的欧氏距离计算(单位转成米) lat_diff = station1.lat - station2.lat lon_diff = station1.lon - station2.lon return (lat_diff**2 + lon_diff**2)**0.5 * 111000 # 1度≈111000米 - 换乘惩罚:每次换乘加一个固定的大值,比如
10000(这个值要远大于两个相邻站点的距离,比如相邻站点一般几百米,所以10000的惩罚会让算法尽量避免换乘)。
启发函数h(n)(保证A*的最优性)
启发函数要满足可采纳性(即永远不高估实际剩余代价),推荐两种方案:
- 基础版:当前站点到终点站点的直线距离(用上面的
calculate_distance计算),这个绝对不会高估,能保证找到最短路径(同时兼顾换乘)。 - 进阶版:如果想让算法更快偏向少换乘的路径,可以估算最少需要的换乘次数×惩罚值 + 直线距离,但要注意估算的换乘次数不能大于实际需要的次数,否则会破坏最优性。
3. 非连通网络的处理逻辑
A*本身就天然支持非连通场景:
- 当你把所有可达的节点都处理完(
open列表为空),但始终没找到终点站点,就直接返回“无可行路径”——说明起点和终点所在的线路属于两个完全不连通的子网络。
4. 具体的A*搜索流程
初始化
- 把起点站点所属的所有线路对应的
NODE加入open列表:比如公交站A在1路和3路,就创建两个NODE,分别对应1路和3路,g(n)初始为0,换乘次数0,父节点为None。 - 初始化
closed列表,用来存储已经处理过的状态(避免重复计算,状态是“站点+线路”的组合)。
循环搜索
- 从
open列表中取出f(n)=g(n)+h(n)最小的节点(可以用优先队列实现)。 - 如果这个节点的站点是终点B,就回溯父节点生成完整路径,结束搜索。
- 如果节点已经在
closed列表里,跳过。 - 把当前节点加入
closed列表,然后扩展两种子节点:- 沿当前线路移动:遍历
BusRoute链表中当前站点的前后站点,计算新的g(n)(当前g(n)+ 站点间距离),换乘次数不变,创建新NODE。如果这个“新站点+当前线路”的状态不在closed,且open列表中没有更优的(比如相同状态但g(n)更小),就加入open。 - 在当前站点换乘:如果当前站点是换乘站(即属于多条
BusRoute),遍历所有其他线路,创建新NODE:g(n)= 当前g(n)+ 换乘惩罚值,换乘次数+1。同样判断状态是否重复,再加入open。
- 沿当前线路移动:遍历
- 重复步骤1-4,直到
open列表为空(无路径)或找到终点。
5. 代码层面的小优化
- 给
BusRoute加个routeId属性,方便快速区分线路。 - 提前构建一个全局映射表:
station_name → list[BusRoute],这样可以快速查到某个站点所属的所有线路,不用每次遍历所有BusRoute。 - 在判断状态是否重复时,用
(station.name, routeId)作为唯一标识,存入closed集合,效率更高。
举个简单例子
假设起点A在1路,终点B在5路,中间需要在C站换乘2路→4路→5路:
- 算法会先沿着1路走到C站,然后因为换乘2路要加10000惩罚,所以会先看看1路有没有其他路径;如果没有,才会考虑换乘2路,然后继续搜索,直到找到5路的B站。
- 因为换乘惩罚很高,算法会优先找换乘次数最少的路径,哪怕总距离稍微长一点(如果有这样的路径)。
内容的提问来源于stack exchange,提问作者Olteanu Radu
相关产品推荐
相关产品推荐

