求一个最短路就如同书上的代码和思路,但是要求多个最短路的话,我们需要维护一个“多维前向节点列表”,在算法执行的过程中如果最小值相同,就把前向节点都保留下来。
类似下图0到3的最短路有两条,pre=[[-1],[0],[0],[1,2]],前向节点列表就是这样的组织形式,列表第三个元素是[1,2],代表3号节点的最短路有两条,分别指向1号节点和2号节点
从pre来遍历得到回溯节点,不同于树的深度或者广度搜索,可以想象有的叶子节点是公用的,无法直接使用深度或者广度搜索来遍历,况且我们的输出是所有从末节点到初始节点的路径,这里可以考虑使用一个栈,栈的元素有两个值,一个值代表当前节点的标号,一个值代表当前节点还有多少个子节点未遍历。
初始入栈的元素是我们的要求得的末尾节点元素,例子里是3,3号节点有两个前置节点未遍历,下面的值就是2,将它的子元素加入栈后,它未遍历节点的值就变成了1,以此类推,直到遇到初始节点(这里是0)就开始弹栈,直到遇到节点的未遍历值不为0时停止弹栈,然后重复上面的过程直至栈空。
class Stack():
def __init__(self):
self.list = []
self.p = -1
def push(self, value, num):
self.p += 1
self.list.append({'value': value, 'num': num})
def pop(self):
if self.p == -1:
return {'value': -1, 'num': 0}
a = self.list[self.p]
del self.list[self.p]
self.p -= 1
return a
def pull(self):
return self.list[self.p]
def pull_replace(self, num):
self.list[self.p]['num'] = num
def is_empty(self):
if self.p == -1:
return 1
return 0
def replace(self, num, value):
self.list[num]['num'] = value
def print(self):
l = self.list[::-1]
o = [item['value'] for item in l]
print(o)
return o
s=Stack()
s.push(num,len(pre[num])) # 输入destination
# pre按照文章中的形式给出即可
while (s.is_empty() != 1):
value = s.pull()['value']
num = s.pull()['num']
next_value = pre[value][-num]
s.pull_replace(num - 1)
next_num = len(pre[next_value])
s.push(next_value, next_num)
if (s.pull()['value'] == 0): # 栈遇到了初始节点,开始打印
s.pull_replace(0)
s.print()
while (s.is_empty() != 1 and s.pull()['num'] == 0):
s.pop()
顺便提一下求次短路径,这里同样维持一个次短路径长度列表和次短路径前向节点回溯列表,主要的思想是一个节点的次短路径是它的所以邻接节点的最短路径值加上路径权重 与 邻接节点的次短路径值加上路径权重,二者取最小(且要大于当前节点的最短路径值)
其中,会出现的问题是环路,比如下面左图,节点1到节点2最短路是1,节点1到节点2的次短路按照bellman-ford算法迭代过程就会变为3,显然1到2是没有次短路的,所以有两种解决方法,一种是在最后迭代的过程中把带环的路去掉(具体做法是每次入栈的节点和栈内节点比较,如果相等就不入栈,并把前节点的子节点个数减一),另一种是在算法遍历的过程中同时维持整条路径,并检测环路,最后直接遍历输出。