算法 - 图论Dijkstra(原理、思路代码实现、以东南大学真题为例讲解手算方法)

Dijkstra

算法原理
Dijkstra算法是一种经典的用于计算单源最短路径的算法。它可以在带权重的图中找到从源节点到所有其他节点的最短路径。Dijkstra算法通过贪心策略,不断选择当前已知最短路径最小的节点,更新其邻接节点的路径长度,直到处理完所有节点。

适用场景

  • 图中的边权重为非负值。
  • 需要计算从单一源点到其他所有节点的最短路径。

算法步骤

  1. 初始化:设定源节点的距离为0,其他所有节点的距离为无穷大。将所有节点标记为未访问状态。
  2. 从未访问节点中选择距离最小的节点作为当前节点。
  3. 对于当前节点的每一个未访问的邻接节点,计算从源节点经过当前节点到达该邻接节点的距离。如果该距离小于之前记录的距离,则更新该距离。
  4. 标记当前节点为已访问状态。
  5. 重复步骤2-4,直到所有节点都被访问过。

实现步骤

  1. 使用一个数组或字典记录每个节点的当前最短路径。
  2. 使用一个优先队列(最小堆)加速获取当前最短路径最小的节点。
  3. 不断从优先队列中取出最短路径最小的节点,更新其邻接节点的路径长度。
  4. 直到优先队列为空,所有节点的最短路径均已确定。

伪代码

function Dijkstra(Graph, source):
    dist[source] = 0
    create priority queue Q
    for each vertex v in Graph:
        if v ≠ source:
            dist[v] = infinity
            prev[v] = undefined
        Q.add_with_priority(v, dist[v])

    while Q is not empty:
        u = Q.extract_min()
        for each neighbor v of u:
            alt = dist[u] + length(u, v)
            if alt < dist[v]:
                dist[v] = alt
                prev[v] = u
                Q.decrease_priority(v, alt)

    return dist, prev

C语言代码

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define V 9  // 图中的顶点数

// 寻找当前未访问节点中距离最小的节点
int minDistance(int dist[], int sptSet[]) {
    int min = INT_MAX, min_index;
    for (int v = 0; v < V; v++) {
        if (sptSet[v] == 0 && dist[v] <= min) {
            min = dist[v], min_index = v;
        }
    }
    return min_index;
}

// 打印从源节点到所有其他节点的最短路径
void printSolution(int dist[]) {
    printf("Vertex \t Distance from Source\n");
    for (int i = 0; i < V; i++) {
        printf("%d \t\t %d\n", i, dist[i]);
    }
}

// 实现Dijkstra算法
void dijkstra(int graph[V][V], int src) {
    int dist[V];  // 存储源节点到其他节点的最短距离
    int sptSet[V];  // 标记节点是否已被处理

    // 初始化所有节点的最短距离为无穷大,sptSet为未访问状态
    for (int i = 0; i < V; i++) {
        dist[i] = INT_MAX, sptSet[i] = 0;
    }
    dist[src] = 0;

    // 遍历所有节点
    for (int count = 0; count < V - 1; count++) {
        int u = minDistance(dist, sptSet);
        sptSet[u] = 1;

        // 更新节点的最短路径
        for (int v = 0; v < V; v++) {
            if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
                dist[v] = dist[u] + graph[u][v];
            }
        }
    }
    printSolution(dist);
}

int main() {
    int graph[V][V] = {{0, 4, 0, 0, 0, 0, 0, 8, 0},
                       {4, 0, 8, 0, 0, 0, 0, 11, 0},
                       {0, 8, 0, 7, 0, 4, 0, 0, 2},
                       {0, 0, 7, 0, 9, 14, 0, 0, 0},
                       {0, 0, 0, 9, 0, 10, 0, 0, 0},
                       {0, 0, 4, 14, 10, 0, 2, 0, 0},
                       {0, 0, 0, 0, 0, 2, 0, 1, 6},
                       {8, 11, 0, 0, 0, 0, 1, 0, 7},
                       {0, 0, 2, 0, 0, 0, 6, 7, 0}};
    dijkstra(graph, 0);
    return 0;
}

代码解释

  • int minDistance(int dist[], int sptSet[]):找到未访问节点中距离最小的节点。
  • void printSolution(int dist[]):打印从源节点到所有其他节点的最短路径。
  • void dijkstra(int graph[V][V], int src):实现Dijkstra算法,从源节点计算到所有其他节点的最短路径。
  • int main():主函数,定义图的邻接矩阵并调用Dijkstra算法计算最短路径。

算法特点

  • 时间复杂度:O(V^2)(使用数组实现优先队列时),使用二叉堆可以优化到O((V + E) log V)。
  • 适用于图中的边权重为非负值的情况。
  • 通过贪心策略逐步找到从源节点到其他节点的最短路径。

通过上述代码和解释,读者应该可以理解Dijkstra算法的原理和实现步骤,并在实际应用中使用该算法计算最短路径。对于考研初试,只需掌握手算方法,我们以东南大学2000年真题为例进行讲解:
在这里插入图片描述

初始状态:

节点距离前驱
a0-
b-
c-
d-
e-
f-
g-

Round 1:

选择距离最小的节点a,更新其邻接节点的距离。

  • 更新节点c的距离为2(0 + 2),前驱为a。
  • 更新节点d的距离为12(0 + 12),前驱为a。
  • 更新节点b的距离为15(0 + 15),前驱为a。
节点距离前驱
a0-
b15a
c2a
d12a
e-
f-
g-

Round 2:

选择距离最小的节点c,更新其邻接节点的距离。

  • 更新节点e的距离为10(2 + 8),前驱为c。
  • 更新节点f的距离为6(2 + 4),前驱为c。
节点距离前驱
a0-
b15a
c2a
d12a
e10c
f6c
g-

Round 3:

选择距离最小的节点f,更新其邻接节点的距离。

  • 更新节点d的距离为11(6 + 5),前驱为f。
  • 更新节点g的距离为16(6 + 10),前驱为f。
节点距离前驱
a0-
b15a
c2a
d11f
e10c
f6c
g16f

Round 4:

选择距离最小的节点e,更新其邻接节点的距离。

  • 节点e没有需要更新的邻接节点。
节点距离前驱
a0-
b15a
c2a
d11f
e10c
f6c
g16f

Round 5:

选择距离最小的节点d,更新其邻接节点的距离。

  • 更新节点g的距离为13(11 + 2),前驱为d。
节点距离前驱
a0-
b15a
c2a
d11f
e10c
f6c
g13d

Round 6:

选择距离最小的节点g,更新其邻接节点的距离。

  • 节点g没有需要更新的邻接节点。
节点距离前驱
a0-
b15a
c2a
d11f
e10c
f6c
g13d

Round 7:

选择距离最小的节点b,更新其邻接节点的距离。

  • 节点b没有需要更新的邻接节点。
节点距离前驱
a0-
b15a
c2a
d11f
e10c
f6c
g13d

最终结果如下:

最短路径结果:

  • a -> a: 0
  • a -> b: 15 (路径:a-b)
  • a -> c: 2 (路径:a-c)
  • a -> d: 11 (路径:a-c-f-d)
  • a -> e: 10 (路径:a-c-e)
  • a -> f: 6 (路径:a-c-f)
  • a -> g: 13 (路径:a-c-f-d-g)
  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Violent-Ayang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值