文章目录

Introduction

加权图

在一些应用中,是非常有用的数据作为带有加权边缘的图形模型。这些图形被称为“加权关系图”。什么是“加权边”,你不知道?考虑一下这个图表:

Dijkstra 算法教程_权重
让我们想象一下,每个节点都是一个城市,每边是两个城市之间的现有道路。这意味着你可以从直接开到B中。但是,你不能开车从A到d直接,因为有这些城市之间没有道路; 相反,例如,你需要去从A到B,然后从B到D.

现在,不是每一条道路是平等的。它们有的长,有的不是在良好的状态(所以你需要慢慢地走),有的有更多的交通…总之:你需要更多的时间用于遍历一些道路比其他。我们可以用分配给道路(图的边缘)的权重来表示那个时间。在这个例子中,我们假设每个数字代表小时的时间量,它需要你采取一条道路。这意味着,从A到B,如果你使用的是直接连接它们的路需要3个小时。

你将会发现,你可以代表很多不同的东西(时间,距离,金钱…),在图形边缘的权重。

路径

假设我问你有多少时间将它带你从C到去B.你可能会说,它会带你7小时,通过采取道路直接连接它们。但是,你也可以说,你可以完成行程中只有4个小时,如果你去从C到A,然后从A到B有可以采取其他途径。

形式上,路径是其连接不同顶点的边缘序列的序列。如果在你的图没有多条边(连接在同一对节点,因为如果你有两个不同的道路直接连接相同的两个城市即平行边),你能描述简单,因为它连接的节点列表中选择一个路径。例如,我们在我们的例子中提到的两个路径是C,B和C,A,B。

最短路径

前面说过,遍历路径C、B需要7个小时,遍历路径C、A、B只需要4个小时。这些时间就是这些路径的权重。因此,我们说一个路径的权重是它所包含的边缘的权重之和。

正如你所看到的,路径C,A,B比路径C,B。其实短,这是C和B之间的最短路径(试图找到一个更短的一个!)。当然,在很多应用中,这将是非常有用的,以便能够提前计算什么两个节点之间的最短路径。什么是我可以带回家的最短路线?什么是我可以从布卡拉曼加飞往夏威夷最便宜的方式?我怎样才能有效地路由从节点1这个数据包到节点2在这个网络中?那样的问题,可以用最短路径算法或变体来解决。

所以…我们怎样才能获得一个图的最短路径?有几种选择。Dijkstra算法就是其中之一!请继续阅读知道如何!

Dijkstra 算法

Dijkstra’s Algorithm 允许您计算图中一个节点(您选择哪一个)和其他每个节点之间的最短路径。您将在本页末尾找到算法的描述,但是,让我们通过一个解释性示例来研究算法!让我们计算节点 C 和图中其他节点之间的最短路径:

Dijkstra 算法教程_权重_02
在算法执行期间,我们将标记每个节点与节点 C(我们选择的节点)的最小距离。对于节点 C,此距离为 0。对于其余节点,由于我们仍然不知道最小距离,因此它开始为无穷大 (∞):

Dijkstra 算法教程_最短路径_03
我们还将有一个当前节点。最初,我们将其设置为 C(我们选择的节点)。在图像中,我们用红点标记当前节点。

现在,我们不按特定顺序检查当前节点(A、B 和 D)的邻居。让我们从 B 开始,我们将当前节点的最小距离(在本例中为 0)与连接当前节点与 B 的边的权重(在本例中为 7)相加,我们得到 0 + 7 = 7 . 我们将该值与 B 的最小距离(无穷大)进行比较;最小值是作为 B 的最小距离保留的值(在这种情况下,7 小于无穷大):

Dijkstra 算法教程_权重_04
到现在为止还挺好。现在,让我们检查邻居 A。我们将 0(当前节点 C 的最小距离)与 1(连接当前节点与 A 的边的权重)相加得到 1。我们将 1 与 A 的最小距离进行比较(无穷大),并保留最小值:
Dijkstra 算法教程_最短路径_05

对 D 重复相同的过程:
Dijkstra 算法教程_权重_06
我们已经检查了 C 的所有邻居。因此,我们将其标记为visited。让我们用绿色复选标记表示访问过的节点:

Dijkstra 算法教程_权重_07
我们现在需要选择一个新的当前节点。该节点必须是最小距离最小的未访问节点(因此,编号最小且没有复选标记的节点)。那是 A。让我们用红点标记它:

Dijkstra 算法教程_最小值_08
现在我们重复这个算法。我们检查当前节点的邻居,忽略访问过的节点。这意味着我们只检查 B。

对于 B,我们将 1(A 的最小距离,我们当前的节点)与 3(连接 A 和 B 的边的权重)相加得到 4。我们将 4 与 B 的最小距离(7)进行比较,并留下最小值:4。

Dijkstra 算法教程_权重_09
之后,我们将 A 标记为已访问并选择一个新的当前节点:D,它是当前距离最小的未访问节点。

Dijkstra 算法教程_最短路径_10
我们再次重复该算法。这一次,我们检查 B 和 E。

对于 B,我们得到 2 + 5 = 7。我们将该值与 B 的最小距离 (4) 进行比较并留下最小值 (4)。对于 E,我们得到 2 + 7 = 9,将其与 E 的最小距离(无穷大)进行比较,留下最小的一个(9)。

我们将 D 标记为已访问并将当前节点设置为 B。

Dijkstra 算法教程_最短路径_11
差不多好了。我们只需要检查E。4 + 1 = 5,它小于E的最小距离(9),所以我们留下5。然后,我们将B标记为已访问并将E设置为当前节点。

Dijkstra 算法教程_最小值_12
E 没有任何未访问的邻居,所以我们不需要检查任何东西。我们将其标记为已访问。

Dijkstra 算法教程_最短路径_13
下面是算法的描述:

  • 用当前距离为 0 标记您选择的初始节点,其余为无穷大。
  • 将当前距离最小的未访问节点设置为当前节点C。
  • 对于N当前节点的每个邻居C:将当前距离C与连接边的权重相加C- N。如果小于 的当前距离N,则将其设置为 的新当前距离N。
  • 将当前节点标记C为已访问。
  • 如果存在未访问的节点,则转至步骤 2。

为了获得对应于这些最小值的路径,我们只需要在每次更改节点的最小距离时跟踪节点。继续阅读以查看它!

跟踪路径

让我们在图中再次运行算法:
Dijkstra 算法教程_最短路径_14
然而,这一次,让我们跟踪实际的最短路径。它们都是空的,除了初始节点的路径,它只包含它:

path to A = empty
path to B = empty
path to C = C
path to D = empty
path to E = empty

新的东西是我们每次修改节点的最小距离时都会更新这些路径。

让我们检查当前节点的邻居。让我们从 B 开始。我们添加 0 + 7 = 7。由于该值小于无穷大,我们用它更改 B 的最小距离,并将当前到 B的路径替换为到当前节点的路径(path to C,即C),加上B。这意味着path to B = C, B.

我们对邻居 A 和 D 重复这个过程。之后,我们的图和路径如下:

Dijkstra 算法教程_最小值_15

path to A = C, A
path to B = C, B
path to C = C
path to D = C, D
path to E = empty

我们的当前节点现在设置为 A。我们检查它唯一的未访问邻居 B。当我们将 B 的最小距离从 7 替换为 4 时,我们也将其当前路径替换为当前节点 A ( C, A)的路径,加B:) path to B = C, A, B。

Dijkstra 算法教程_最短路径_16
我们的当前节点现在设置为 A。我们检查它唯一的未访问邻居 B。当我们将 B 的最小距离从 7 替换为 4 时,我们也将其当前路径替换为当前节点 A ( C, A)的路径,加B:) path to B = C, A, B。

Dijkstra 算法教程_最短路径_17

path to A = C, A
path to B = C, A, B
path to C = C
path to D = C, D
path to E = empty

我们将 A 标记为已访问并选择我们的下一个当前节点:D。我们检查两个邻居:B 和 E。

检查 B 时,我们不替换其最小距离(因为现有 4 小于计算出的 7),因此我们也不替换其当前路径。请记住:我们仅在修改节点的最小距离时才替换路径。

然后我们检查邻居 E,更新它的最小距离(9,它小于无穷大)和路径(path to E = C, D, E,它是path to D加 E),剩下这个:

Dijkstra 算法教程_最小值_18

path to A = C, A
path to B = C, A, B
path to C = C
path to D = C, D
path to E = C, D, E

让我们快进一点:我们继续应用算法直到我们完成。完成后,我们的图形和路径将如下所示:

Dijkstra 算法教程_权重_19

path to A = C, A
path to B = C, A, B
path to C = C
path to D = C, D
path to E = C, A, B, E

结尾

再次是 Dijkstra 算法:

  • 用当前距离为 0 标记您选择的初始节点,其余为无穷大。
  • 将当前距离最小的未访问节点设置为当前节点C。
  • 对于N当前节点的每个邻居C:将当前距离C与连接边的权重相加C- N。如果小于 的当前距离N,则将其设置为 的新当前距离N。
  • 将当前节点标记C为已访问。
  • 如果存在未访问的节点,则转至步骤 2。

让我们看看我们可能应用于算法的一些小改进:

  • 如果您只需要两个特定节点之间的路径,则可以在将第二个节点标记为已访问后立即停止算法。
  • 有时,两个节点之间有几条最小路径(权重相同的不同路径)。如果您愿意,您可以跟踪所有这些变体:在步骤 3 中,如果计算值与当前距离之间存在联系,则保存旧的当前路径和新路径。这会使跟踪复杂化,但它可能对您有用。
  • 如果因为没有剩余的单向节点而完成算法,但存在最小距离仍然为无穷大的节点,则这些节点没有任何到原始节点的有效路径。