背景知识
图简介
图由节点和边组成,边有方向的图称为有向图,边没有方向的图称为无向图,最短路径算法里可以把无向图视为双向连接的有向图。
边有权重的图称为有权图,边没有权重的图称为无权图,无权图可以视为边的权重均为1的图。
单源点最短路径
给定图中的一个节点,求该节点到其他所有节点的最短路径。
Bellman-Ford算法
概述
Bellman-Ford属于DP算法,适用于含负权边的图,并且可以检测图中是否含有负权环(意味着没有最短路径),时间复杂度为O(VE)
核心思想
对所有边进行V-1次遍历,第一次遍历获得边数为1的最短路径,第二次遍历获得边数为2的最短路径,以此类推,第V-1次遍历获得边数为V-1的最短路径。V-1次遍历后,进行第V次遍历,如果能够获得更小的路径长度,说明存在负环。
算法步骤
dis[]用于存储每个节点到源点src的距离,preNode[]存储每个节点的前驱节点
Step1:初始化dis[]为INT_MAX,dis[src]初始化为0. preNode[src]初始化为-1.
Step2:遍历图中的每条边edge(v,u),若dis[v]不为INT_MAX,且dis[v] + weight(v,u) < dis[u],执行dis[u] = dis[v] + weight(v,u)。
Step3:重复Step2 V次,若某次遍历没有执行dis[u] = dis[v] + weight(v,u),即发生了松弛,结束Step3
Step4:判断Step3最后一次执行是否发生松弛,若发生,说明图中含有负权环,若没有发生,说明图中没有负权环。
为什么能获得最短路径
可以从直观上理解,第一次遍历获得了(距离源点)边数量为1的最短路径,第二次在第一次的基础上获得了边数量为2的最短路径,第三次在第二次的基础上获得了边数量为3的最短路径,依次类推,有些节点到源点有很多条路径,这些路径的边数可能不一样也可能一样,算法会在V-1循环中比较这些路径的长度,取最小值作为最短路径(遍历中边数多的可能距离可能反而小于边数少的,这个时候就会发生松弛了)。
为什么最后一次遍历是否发生松弛可以判断负环
有两种情况会导致Step3结束,一是当次遍历没有发生松弛,二是已经循环了V次,情况一显然不存在负环,不然肯定可以继续松弛。情况二下,如果最后一次遍历发生了松弛,说明第V次松弛了,意味着找到的边长数为V的最短路径,而最长的最短路劲应该是V-1条边, 所以肯定至少一条边重复了,一定是出现环了,环路径比非环更短,说明是负环。
举个最简单的例子:
第一次遍历 dis[1] = 2, dis[2] =INT_MAX
第二次遍历 dis[1] = 2, dis[2] = 3;
已经遍历了V-1即两次了,第3次遍历,dis[1] = dis[2] + wegiht(2,1) = 1,小于dis[1],所以会继续发生松弛。
如果weight(1,2) = 3,环长度为3-2=1,正环,这个时候第V次遍历就不会发生松弛了。