CLRS中的Dijkstra算法,第59页在第7行中有以下代码:
for each vertex v \in Adj[u]
此行选择迭代节点v的所有邻居。此处的节点v是算法当前正在处理并添加到最短路径树的节点。 然而,在v的那些邻居中,已经在集合S中的那些邻居在&完成,和那些 S中的节点正在形成最短路径树T. 集合S中的任何节点都不能具有比T中已经存在的路径短的路径到v。 否则,该路径将一直遍历到那时。
所以,第7行不应该更好
for each vertex v \in Adj[u] \intersect Q // Q = V \ S
或同样地
for each vertex v \in Adj[u]\S
// ===========================
添加解释:
处理完毕后(已处理=设置距离和父矢量 所有直接邻居的条目)节点u并将其添加到树中, 节点u离源最近。如果有一个off-tree节点 因此,通过它在源和之间存在一条较短的路径。 ü,节点z将在u之前处理。
// ======================
附加2:对Javier下面有用答案的冗长评论:
将图中的所有边放在一个数组中,说“EDGES” - 一个边,一个数组条目。 每个数组条目保存边(u,v),边权重和2个指针 - 一个到节点u,一个到节点v。
图表仍然表示为邻接列表。
Adj [u]仍然是一个链表 - 但是,这个链表在一个数组结构上。 此时,此列表中的节点值是与该边对应的EDGES的索引。
因此,例如,Node u有2个链接事件: (你,x)& (U,Y)。 Edge(u,x)位于EDGES的第23个单元格,(u,y)位于第5个单元格。 然后,Adj [u]是长度为2的链表,该列表中的节点为23和5. Say,Adj [u] [0] .edgesIndex = 23和Adj [u] [1] .edgesIndex = 5。这里,对于Adj [x]的链表中的一些i,Adj [x] [i] .edgesIndex = 23。 (Adj [j] [i],作为链表中的节点,还有“next”和“prev”字段。)
并且,EDGES [23]对Adj [u]的相应条目(u,x)有一个引用,而Adj [x]的条目另一个引用。我按原样离开第7行,但这一次,在我处理了该循环中的边(u,v)之后(我已经从Adj [u]中发现了这个边缘),我从链接列表中删除了该边缘Adj [u],从那里我转到相应的EDGES条目,该条目具有对应的Adj [x] [i]条目的引用。我删除所有 - EDGES [23],Adj [u] [0]和Adj [x] [i](无论我在那里。)对于所有数组结构,我可以在每个边缘的恒定时间内处理所有这些。
仍然是邻接列表表示,可以从(u,v)跟踪(v,u)的位置并在恒定时间移除它们,现在仅在该交叉点的边缘处理我正在寻找渐近的使用的内存量相同,时间效率更高。
// ====================
附加3:
纠正上述附加2中的一件事:
我写的内容可能需要更多 - 而不是没有它的算法:
删除Adj [u]和Adj [x]中链接列表中的链接,以及相应的链接 EDGES条目,所有这些中的直接内存查找不太可能 占用较少的CPU周期,而不是放松算法中的边缘。
它仍会检查每个边缘(u,v)一次而不是两次 - 一次用于(u,v),一次用于(v,u),并且与没有它的算法明显处于相同的渐近时间。但是绝对没什么收获 处理时间和使用的内存成本更高。
另一种选择是: 添加一行类似
的内容if (v \in S) then continue;
作为for循环的第一个。这可以通过将S维持为来实现 布尔值的S [| V |]数组,并在每个顶点相应时设置其值 添加到设置S--这基本上是哈维尔在他的ans中所说的。
答案 0 :(得分:1)
将Adj[u]
与Q
相交是正确的,但这不是一个好主意,因为最后,您需要迭代Adj[i]
的所有元素。我认为没有办法解决这个问题。
只有你能找到一种有效地交叉这两个 VERY 的方法,即比O(n)更好的方法,它才有效。
您实现的一个很好的增强功能是标记所有已结算的节点,然后如果节点v
已确定,则可以忽略内部循环的其余部分。