跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 图论实战:三种经典最短路径算法解析

C++ 图论中最短路径问题的三种核心解法。Dijkstra 适用于非负权图,采用贪心策略高效求解单源路径;Bellman-Ford 支持负权边并能检测负权回路,适合更复杂场景;Floyd-Warshall 则用于计算任意两点间的最短距离,基于动态规划思想。通过 C++ 代码详解各算法原理、实现细节及适用边界,帮助开发者根据实际图结构选择最优方案。

月光旅人发布于 2026/3/23更新于 2026/5/96 浏览
C++ 图论实战:三种经典最短路径算法解析

在带权有向图中,寻找从起点到终点的路径使得边权总和最小,这是图论中最基础也最实用的问题之一。根据图的特性(是否有负权边、是否需要全源路径等),我们通常有三种经典算法可供选择:Dijkstra、Bellman-Ford 和 Floyd-Warshall。

Dijkstra 算法

Dijkstra 是单源最短路径的经典贪心算法。它适用于没有负权边的图。核心思想是从起点出发,每次从未确定最短路径的节点中选出距离最小的一个,用它去'松弛'所有邻接点,标记该节点为已确定,直到所有节点处理完毕。

在实际实现中,我们需要维护一个距离数组和一个前驱数组。初始化时,起点距离为 0,其余为无穷大。每一轮迭代都扫描未访问节点找到当前最小值,然后更新邻居的距离。如果某条路径通过当前节点能更短地到达邻居,就更新距离并记录前驱。

// src 是选定的起点,dist 记录起点到各点的最短路径,pPath 记录到每个点的最短路径的前驱顶点下标
void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath) {
    size_t srci = GetVertexIndex(src);
    size_t n = _vertexs.size();
    
    dist.resize(n, MAX_W);
    pPath.resize(n, -1);
    dist[srci] = 0;
    pPath[srci] = srci; // 已经确定最短路径的顶点集合
    
    vector<bool> S(n, false);
    for (size_t j = 0; j < n; ++j) {
        // 选最短路径顶点且不在 S 更新其他路径
        int u = 0;
        W min_dist = MAX_W;
        for (size_t i = 0; i < n; ++i) {
            if (!S[i] && dist[i] < min_dist) {
                u = i;
                min_dist = dist[i];
            }
        }
        
        S[u] = true;
        
        // 松弛更新 u 连接顶点 v
        // srci -> u + u -> v < srci -> v 更新
        for (size_t v = 0; v < n; ++v) {
             (!S[v] && _matrix[u][v] != MAX_W && dist[u] + _matrix[u][v] < dist[v]) {
                dist[v] = dist[u] + _matrix[u][v];
                pPath[v] = u;
            }
        }
    }
}
if

需要注意的是,一旦遇到负权边,Dijkstra 的贪心策略就会失效,因为后续可能通过负权边获得更短路径,导致之前确定的'最短'被推翻。

Bellman-Ford 算法

当图中存在负权边时,Dijkstra 就不适用了。Bellman-Ford 算法可以解决带负权图的单源最短路径问题,并且能检测负权回路。它的本质是对所有边进行重复松弛,最多执行 n-1 次(n 为顶点数),因为任何简单路径最多包含 n-1 条边。

如果在第 n 次松弛后还能更新距离,说明图中存在负权回路,此时最短路径不存在。虽然时间复杂度比 Dijkstra 高,但它的鲁棒性更强。

bool BellmanFord(const V& src, vector<W>& dist, vector<int>& pPath) {
    size_t n = _vertexs.size();
    size_t srci = GetVertexIndex(src);
    
    // vector<W> dist, 记录 srci-其他顶点最短路径权值数组
    dist.resize(n, MAX_W);
    // vector<int> pPath 记录 srci-其他顶点最短路径父顶点数组
    pPath.resize(n, -1);
    
    // 先更新 srci->srci 为缺省值
    dist[srci] = W();
    
    // 总体最多更新 n 轮
    for (size_t k = 0; k < n; ++k) {
        bool update = false;
        cout << "更新第:" << k << "轮" << endl;
        for (size_t i = 0; i < n; ++i) {
            for (size_t j = 0; j < n; ++j) {
                // srci -> i + i ->j
                if (_matrix[i][j] != MAX_W && dist[i] != MAX_W && dist[i] + _matrix[i][j] < dist[j]) {
                    update = true;
                    //cout << _vertexs[i] << "->" << _vertexs[j] << ":" << _matrix[i][j] << endl;
                    dist[j] = dist[i] + _matrix[i][j];
                    pPath[j] = i;
                }
            }
        }
        // 如果这个轮次中没有更新出更短路径,那么后续轮次就不需要再走了
        if (!update) break;
    }
    
    // 还能更新就是带负权回路
    for (size_t i = 0; i < n; ++i) {
        for (size_t j = 0; j < n; ++j) {
            // srci -> i + i ->j
            if (_matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j]) {
                return false;
            }
        }
    }
    return true;
}

Floyd-Warshall 算法

如果你需要计算图中任意两点之间的最短路径,Floyd-Warshall 是最直接的选择。它采用动态规划的思想,依次把每个点当作中转点,判断从 i 到 j 是直接走更近,还是经过这个中转点 k 再走更近。

三层循环结束后,我们就得到了全图所有点对的最短距离矩阵。虽然时间复杂度是 O(n³),对于稠密图或点数较少的情况非常高效,且代码实现极其简洁。

void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath) {
    size_t n = _vertexs.size();
    vvDist.resize(n);
    vvpPath.resize(n);
    
    // 初始化权值和路径矩阵
    for (size_t i = 0; i < n; ++i) {
        vvDist[i].resize(n, MAX_W);
        vvpPath[i].resize(n, -1);
    }
    
    // 直接相连的边更新一下
    for (size_t i = 0; i < n; ++i) {
        for (size_t j = 0; j < n; ++j) {
            if (_matrix[i][j] != MAX_W) {
                vvDist[i][j] = _matrix[i][j];
                vvpPath[i][j] = i;
            }
            if (i == j) {
                vvDist[i][j] = W();
            }
        }
    }
    
    // 最短路径的更新 i-> {其他顶点} ->j
    for (size_t k = 0; k < n; ++k) {
        for (size_t i = 0; i < n; ++i) {
            for (size_t j = 0; j < n; ++j) {
                // k 作为的中间点尝试去更新 i->j 的路径
                if (vvDist[i][k] != MAX_W && vvDist[k][j] != MAX_W && vvDist[i][k] + vvDist[k][j] < vvDist[i][j]) {
                    vvDist[i][j] = vvDist[i][k] + vvDist[k][j];
                    // 找跟 j 相连的上一个邻接顶点
                    // 如果 k->j 直接相连,上一个点就 k,vvpPath[k][j] 存就是 k
                    // 如果 k->j 没有直接相连,k->...->x->j,vvpPath[k][j] 存就是 x
                    vvpPath[i][j] = vvpPath[k][j];
                }
            }
        }
    }
}

总结来说,Dijkstra 适合非负权单源场景,效率高;Bellman-Ford 能处理负权并检测环,但较慢;Floyd 则用于全源最短路径,代码简单但开销较大。根据具体业务场景选择合适算法,往往比盲目追求最优解更重要。

目录

  1. Dijkstra 算法
  2. Bellman-Ford 算法
  3. Floyd-Warshall 算法
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 2023 年度编程语言榜单发布:Python 连续八年位居榜首
  • 力扣 234. 回文链表
  • 分治算法实战:归并排序与逆序对问题
  • Java Stream API 排序流特性与用法
  • 基于 Jekyll 搭建个人 GitHub 学术主页
  • Open WebUI 新技术 MCPo:实现 MCP 协议到 OpenAPI 的无缝集成
  • Claude Code 与 OpenClaw 实战指南:从初始化到自动化工作流
  • LeetCode 每日一题:最小位数组问题解法详解
  • Spring Cloud Sentinel 熔断降级实战与原理解析
  • FPGA 实现多协议编码器接口:BISS-C、SSI 与多摩川集成设计
  • 多种编程语言数组遍历实现对比
  • 4x Tesla P40 上训练 Llama-3.3-70B 大模型指南
  • Java Cookie 技术原理与应用
  • Fast-GitHub 浏览器插件安装与配置指南
  • YOLOv8/v11 结合 PaddleOCR 实现车牌检测与识别系统实战
  • Python 全栈开发学习路线与核心技术体系解析
  • 视觉语言模型(VLM)综述:An Introduction to Vision-Language Modeling
  • 基于 SFT 微调 Llama2 实现自我认知
  • Win11 本地部署 OpenClaw:集成 Telegram 机器人与网页搜索功能
  • Rust 核心基础数据类型与变量系统

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online