总结:
方法1: 引入path数组, 记录下每个节点 想要走最短路径到达终点的下一跳节点;
方法2: 从起点开始遍历, 遍历邻接节点, 如果当前节点到下一节点的距离+下一个节点到终点的距离=当前节点到终点的距离, 则认为该下一节点是最优路径上的节点
一般情况下, Dijkstra算法是解决单源最短路径问题的, 也就是在已知终点时, 求出图中的每个点到终点的最短距离, 但是一般只记录距离值, 不会记录具体的路径, 即怎么走能从起点通过最短路径来到达终点;
思路就是引入一个path表, 其中path[x]表示, 如果从x节点要到达终点的最短路径, 在x节点下一步应该怎么走, 获得这个path表之后, 我们只需要从起点回溯这个path表, 直到终点位置, 这样就能找到从起点到终点的最短路径了; 类似于下面这样的操作:
nodes = []
u = nf
while u!=nt:
nodes.append(u)
u = path[u]
nodes.append(nt)
先放一段普通的Dijkstra算法的python实现
def generateDisArray(nf, nt, INF=float("inf")):
dis = dict((k.getID(),INF) for k in net.getNodes())
heap = []
# u is the next node to be relaxed
u = nf
dis[u] = 0
heapq.heappush(heap, [0, u])
while len(heap)>0:
p = heapq.heappop(heap)
pid = p[1]
pdis = p[0]
if dis[pid] < pdis :
continue
for x in AdjacencyList[pid]:
if dis[x] > dis[pid]+AdjacencyList[pid][x]:
dis[x] = dis[pid]+AdjacencyList[pid][x]
# 这里的if判断是为了处理一些没有出度的点, 这些点不可能作为中间节点来松弛
if AdjacencyList.__contains__(x)==True:
heapq.heappush(heap, [dis[x], x])
if pid==nt:
print("Good Luck!!!!")
return dis
return dis;
这里我加了一个小剪枝, 因为我其实只希望找从节点nf到节点nt的最短路径, 所以我第一次对nf节点做松弛操作的时候, 其实已经可以确定此时已经找到了到nf到nt的最短路了, 后面的循环都不需要了;
然后上改进之后的Dijkstra算法:
def generateDisArray(nf, nt, INF=float("inf")):
dis = dict((k.getID(),INF) for k in net.getNodes())
path = {}
heap = []
# u is the next node to be relaxed
u = nf
dis[u] = 0
heapq.heappush(heap, [0, u])
while len(heap)>0:
p = heapq.heappop(heap)
pid = p[1]
pdis = p[0]
if dis[pid] < pdis :
continue
for x in AdjacencyList[pid]:
if dis[x] > dis[pid]+AdjacencyList[pid][x]:
dis[x] = dis[pid]+AdjacencyList[pid][x]
path[x] = pid
# 这里的if判断是为了处理一些没有出度的点, 这些点不可能作为中间节点来松弛
if AdjacencyList.__contains__(x)==True:
heapq.heappush(heap, [dis[x], x])
if pid==nt:
print("Good Luck!!!!")
return dis, path
return dis, path;
这里自己在第一次写的时候犯了一个错误, 当时想当然的认为, 求出dis数组之后, 只要从起点开始, 贪心的去找邻接的点中dis值小的节点, 直到找到终点位置, 但是其实这个想法是错误的, 因为这样贪心的策略其实只考虑到了下一步的利益, 当前一步的消耗是没有考虑的
想到这里, 我考虑到, 那如果把当前的消耗考虑进去是不是就可以了呢? 应该是可以的, 寻找最优路径的实现如下
dis = generateDisArray(nf, nt)
edges = []
u = net.getNode(nt)
while u.getID()!=nf:
for e in u.getIncoming():
if e.allows(newVehicletype)==False:
continue;
#这里的意思就是从当前节点到下一节点的消耗, 加上下一节点的dis值如果等于当前节点的dis值
#那么就认为走这个节点是最优的路径
if e.getLanes()[0].getLength()+dis[e.getFromNode().getID()]==dis[u.getID()]:
edges.append(str(e.getID()))
u = e.getFromNode()
break