今天发现两算法用队列优化时表现出许多相似性,而且队列优化思想与未优化的思想有所不同,容易产生误解,故总结一下
Floyd算法核心
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
循环过程,k是中转点,故核心思想为从只能通过1中转,只能通过1,2中转……只能通过1,2,3……n中转。
队列优化的代码在之前的文章中已经给出,这里对比一下两者的不同。
观察很容易发现这里通过k点中转,dis[k][j] 可以使k经过其他中转点(1,2,3……k-1)到达j对应的距离,即k->……->j;
而在队列优化代码中,比较的是 dis[u][v]>dis[u][id]+w[i](此处id是中转点),其中w[i]是指id->v的距离,并且中间不会经过中转点
两者的差异是否会造成结果错误呢?
答案是不会的,考虑这样一个过程
在不用队列的算法中,首先经过1中转可以减少到4的距离,一轮循环后,中转点变成2,在上一轮循环中,2->4也被优化为2->1->4,此时的dis[2][4]显然已经不是2直接到4的距离了,之后再次优化dis[3][4],得最下图所示路径;下面我们看一下用队列优化会有什么效果。
首先在第一轮中,3->4、3->1和3->2的距离发生改变,故节点1、4、2入队(这里4出队与结果无关,故不考虑4)。情况一:1先出队,根据出边优化得3->1->4,之后2出队,出边继续优化发现3->1可以优化为3->2->1,此时节点3到节点1的距离再次发生改变,故节点1又入队,再出队后边优化到了图中最后一步的情况。情况二更好考虑,故不再赘述。
比较队列优化没有bug的原因,队列优化能通过节点的重复入队,来不断优化两点间的距离,故尽管出边优化过程中出边的到终点的距离都是没有经过中转点的,其仍然可以得到正确结果。
dij算法核心
每次找到距离起点最近的估计值的点,设为确定值,并将其作为中转点优化起点到其他点的距离。
找最近的过程可以用堆优化,即优先队列。
可以发现两者使用队列的方式是十分相似的,都是用在队列中的节点的出边来优化,但是不同之处在于,dij算法中出队的永远都是队中距离起点最近的,并且每个节点只会入队一次,而Floyd算法每个节点可以入队多次,并无需确保出队节点距离起点的远近,这决定了Floyd算法可以处理负权边,而dij不行