C++ dijkstra 最短路径算法

一:概述      

        Dijkstra 算法是由 荷兰计算机科学家 Edsger W. Dijkstra(艾兹赫尔·戴克斯特拉) 在 1956 年 发明的,并于 1959 年正式发表。Dijkstra 算法 是一种用于计算图中 单源最短路径(Single-Source Shortest Path) 的经典算法。也就是说,它可以求出从一个起点到图中所有其他点的最短路径。

        Dijkstra 算法的核心思想是:贪心策略 —— 每次总是选择当前已知的距离起点最近的未访问节点,并尝试更新其邻居的最短路径。 

        

     算法步骤(以邻接表 + 最小堆实现为例):

  1. 初始化所有点到源点的距离为 ∞(无穷大),源点到自身为 0;

  2. 使用 最小堆(优先队列) 选择当前距离源点最短的节点;

  3. 对其所有邻居节点尝试 “松弛”操作(即更新邻居最短路径);

  4. 将更新后的节点重新加入堆中;

  5. 重复步骤 2~4,直到所有点都被访问或目标点被访问;

  6. 返回目标点的距离。

二:实现

#include <cassert>
#include <iostream>
#include <limits>
#include <queue>
#include <utility>
#include <vector>

constexpr int64_t MAX = std::numeric_limits<int64_t>::max();

namespace graph {

    using adjacency_list = std::vector<std::vector<std::pair<int, int>>>;

    /**
     * @brief 向图中添加一条有向边
     * @param adj 图的邻接表
     * @param u 起点编号(1-based)
     * @param v 终点编号(1-based)
     * @param w 边的权重
     */
    void add_edge(adjacency_list& adj, int u, int v, int w) {
        adj[u - 1].emplace_back(v - 1, w);
    }

    /**
     * @brief 使用 Dijkstra 算法计算起点 s 到终点 t 的最短路径长度
     * @param adj 图的邻接表
     * @param s 起点编号(0-based)
     * @param t 终点编号(0-based)
     * @return 最短路径长度;不可达时返回 -1
     */
    int dijkstra(const adjacency_list& adj, int s, int t) {
        int n = static_cast<int>(adj.size());
        std::vector<int64_t> dist(n, MAX);

        // 优先级队列(最小堆):元素为 pair<距离, 顶点编号>
        std::priority_queue<std::pair<int64_t, int>,
            std::vector<std::pair<int64_t, int>>,
            std::greater<>>
            pq;

        dist[s] = 0;
        pq.emplace(0, s);

        while (!pq.empty()) {
            auto [current_dist, u] = pq.top();
            pq.pop();

            // 如果当前距离大于已知距离,说明此条路径已过时,跳过
            if (current_dist > dist[u]) continue;

            // 松弛操作
            for (const auto& [v, weight] : adj[u]) {
                if (current_dist + weight < dist[v]) {
                    dist[v] = current_dist + weight;
                    pq.emplace(dist[v], v);
                }
            }
        }

        return dist[t] == MAX ? -1 : static_cast<int>(dist[t]);
    }

    /**
     * @brief 内置测试函数
     */
    int run_tests() {
        std::cout << "Running predefined tests...\n";

        {
            std::cout << "Test 1...\n";
            adjacency_list adj(5);
            add_edge(adj, 1, 2, 3);
            add_edge(adj, 1, 3, 8);
            add_edge(adj, 2, 4, 2);
            add_edge(adj, 3, 5, 10);
            add_edge(adj, 4, 5, 1);
            assert(dijkstra(adj, 0, 4) == 6);  // 1->2->4->5 总权重 3+2+1=6
            std::cout << "Test 1 passed.\n";
        }

        {
            std::cout << "Test 2...\n";
            adjacency_list adj(4);
            add_edge(adj, 1, 2, 1);
            add_edge(adj, 2, 3, 4);
            add_edge(adj, 3, 4, 1);
            add_edge(adj, 1, 4, 10);
            assert(dijkstra(adj, 0, 3) == 6);  // 1->2->3->4 总权重 1+4+1=6
            std::cout << "Test 2 passed.\n";
        }

        {
            std::cout << "Test 3 (Unreachable)...\n";
            adjacency_list adj(3);
            add_edge(adj, 1, 2, 5);
            // 3号节点孤立,无法到达
            assert(dijkstra(adj, 0, 2) == -1);  // 1->3 不可达
            std::cout << "Test 3 passed.\n";
        }

        std::cout << "All tests passed successfully.\n";
        return 0;
    }


}  // namespace graph

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

黑不溜秋的

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

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

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

打赏作者

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

抵扣说明:

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

余额充值