算法学习笔记(三) 最短路 Dijkstra 和 Floyd 算法

本文详细介绍了图论中的经典问题——最短路径求解,包括Dijkstra算法和Floyd算法的基本原理、时间复杂度、空间复杂度及实际应用。通过实例演示算法步骤,并提供C语言实现的源代码,旨在帮助读者快速掌握并实践这些核心算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图论中一个经典问题就是求最短路,最为基础和最为经典的算法莫过于 Dijkstra 和 Floyd 算法,一个是贪心算法,一个是动态规划,这也是算法中的两大经典代表。用一个简单图在纸上一步一步演算, 带回溯的笔记在这篇

 

对于平时的练习,一个很厉害的 ACMer 说:“刷水题可以加快我们编程的速度,做经典则可以让我们触类旁通,初期如果遇见很多编不出,不妨就写伪代码,理思路,在纸上进行整体分析和一步步的演算,然后在转换成代码,再反复迭代”。Linus 不是也说了 "Nobody actually creates perfect code the first time around, except me. But there’s only one of me."  ^_^  对于算法的学习,绝大多数都是把问题分解变形,然后套用以前或别人的思路。只有把经典的算法都烂熟于心时,解决问题时才可以做到不知不觉的套用已有的思路和经验。能让我们走的更远的只有热情和方法!当认准一件事有价值时,我们要做的就是长期坚持,既然熟练掌握经典算法这么有价值,那么接下来要做的就是反复训练直到烂熟于心 ^_^

 

单源最短路

给定起点 start, 求到任意点的最短路 Dijkstra 算法,前提不能有负权边和孤立点:

  • 贪心算法:每次找最近的点,局部最优等于全局最优,数学归纳法可证
  • 维护起点 start 到每个点的距离
  • 时间复杂度 O(n^2)
  • 附加空间复杂度 O(n)
Dijkstra 算法伪代码:
Q = {}  // 已求出到 start 点最短路的点集合,初始为空
d[s] = 0, 其余值为正无穷大
while (|Q| < |V|)  // 数学符号|A|表示集合A的点数
    取出不在Q中的最小的d[i]
    for (i相邻的点j,j不属于Q)
        d[j] = min(d[j], d[i] + c[i][j]) //维护距离
    Q = Q + {i}

 

全局最短路

求出图中任意两点最短路,利用 Floyd 算法,对负权边仍然有效:

  • 动态规划:每次加入一个点
  • 维护任意两点间的距离
  • 时间复杂度 O(n^3)
  • 附加空间复杂度 O(n^2)
Floyd 算法伪代码:        # 直接改成 Python 了,没办法就是喜欢 Python :)
for k in range(0, n):			
    for i in range(0, n):
        for j in range(0, n):            
            g[i][j] = min(g[i][j], g[i][k]+g[k][j])

小实验

一个图如下所示:

源代码 ( C 实现)

 

#include <stdio.h>

void Dijkstra(const void *graphRaw, int vertexNum, int maxDistance,
              int start, int *distance) {
    const int *graph = (const int *)graphRaw;
    int visited[vertexNum];
    int i, min, nearest;
    int visitedNum = 0;

    // start --> other, all not visited
    for (i = 0; i < vertexNum; i++) {
        distance[i] = graph[start * vertexNum + i];
        visited[i] = 0;
    }

    while (visitedNum < vertexNum) {
        // find nearest vertex to start in {notVisited}
        min = maxDistance;
        nearest = -1;
        for (i = 0; i < vertexNum; i++) {
            if (!visited[i] && distance[i] < min) {
                nearest = i;
                min = distance[i];
            }
        }
        if (nearest == -1) return;
        visited[nearest] = 1;
        visitedNum++;

        // update distance of vertex that is not visited
        for (i = 0; i < vertexNum; i++) {
            if (!visited[i] &&
                distance[i] > distance[nearest] + graph[nearest*vertexNum+i]){
                distance[i] = distance[nearest] + graph[nearest*vertexNum+i];
            }
        }
    }
}

// write the result to graph
void Floyd(int graph[9][9]) {
    int num = 9, i, j, k;
    for (k = 0; k < num; ++k) {           // middle
        for (i = 0; i < num; ++i) {       // start
            for (j = 0; j < num; ++j) {   // end
                if (graph[i][j] > graph[i][k] + graph[k][j]) {
                    graph[i][j] = graph[i][k] + graph[k][j];
                }
            }
        }
    }
}

int main() {
    int N = 65536;
    int graph[9][9] = {
            {0, 1, 5, N, N, N, N, N, N},
            {1, 0, 3, 7, 5, N, N, N, N},
            {5, 3, 0, N, 1, 7, N, N, N},
            {N, 7, N, 0, 2, N, 3, N, N},
            {N, 5, 1, 2, 0, 3, 6, 9, N},
            {N, N, 7, N, 3, 0, N, 5, N},
            {N, N, N, 3, 6, N, 0, 2, 7},
            {N, N, N, N, 9, 5, 2, 0, 4},
            {N, N, N, N, N, N, 7, 4, 0}
    };
    int start = 0, distance[9], i, j;
    Dijkstra(graph, 9, N, start, distance);
    printf("vertex %d to other vertex: \n", start);
    for (i = 0; i < 9; ++i) {
        printf("%d ", distance[i]);
    }

    Floyd(graph);
    printf("\nall vertex to other vertex distance\n");
    for (i = 0; i < 9; ++i) {
        for (j = 0; j < 9; ++j) {
            printf("%d ", graph[i][j]);
        }
        printf("\n");
    }

    return 0;
}

全文完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值