最短路径问题00:dijkstra算法

Dijkstra算法是解决单源最短路径问题的经典算法。

一、算法核心思想

Dijkstra算法的核心是**贪心策略**和**动态规划**的结合:

  1. **贪心选择**:每次选择当前距离源点最近的未访问节点

  2. **最优子结构**:最短路径的子路径也是最短路径

  3. **松弛操作**:通过新加入节点更新其他节点的距离估计

```mermaid

graph TD

A初始化 --> B选择最近节点

B --> C{所有节点已处理?}

C -->|否| D松弛操作更新邻居

D --> B

C -->|是| E输出结果

三、算法精髓详解

  1. **贪心选择的正确性证明**:
  • 设当前未访问节点中距离最小者为v

  • 假设存在更短路径:源点→...→w→v(w未访问)

  • 但distw ≥ distv(因为v

是最小值),矛盾!

  • ∴ v的最短路径已确定
  1. **松弛操作的本质**:

```math

\text{dist}j = \min(\text{dist}j, \text{dist}v + w(v,j))

```

  • 这是算法中的**状态转移方程**

  • 基于三角不等式:源点到j的距离 ≤ 源点到v的距离 + v到j的距离

  1. **前驱数组的妙用**:
  • 存储路径信息:`pj = v` 表示在最短路径上,j的前驱是v

  • 路径重构:递归回溯 `pj → pv → ... → 源点`

代码示例:

输入图中节点个数和节点之间的边数,然后根据提示输入不同节点之间每个边的起点和终点以及两点之间的距离,然后输入源点,使用dijkstra算法求解并打印出输入源点到不同节点的最短距离以及路径:

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <stack>

using namespace std;

const int N = 1005;
const int INF = 0x3FFFFFFF; // 更合适的无穷大值
int G[N][N], dist[N];
int p[N]; 
int n, m; 
bool flag[N]; 

void dijkstra(int u)
{
    // 初始化
    for (int i = 1; i <= n; ++i) {
        dist[i] = G[u][i];
        flag[i] = false;
        if (dist[i] == INF) {
            p[i] = -1;
        } else {
            p[i] = u;
        }
    }
    dist[u] = 0;
    flag[u] = true;
    // 主循环
    for (int i = 1; i < n; ++i) {
        int minDist = INF, minNode = -1;
        // 在未访问节点中寻找距离最小的节点
        for (int j = 1; j <= n; ++j) {
            if (!flag[j] && dist[j] < minDist) {
                minDist = dist[j];
                minNode = j;
            }
        }
        // 所有节点已访问或剩余节点不可达
        if (minNode == -1) return;
        flag[minNode] = true;
        // 松弛操作:更新相邻节点的最短距离
        for (int j = 1; j <= n; ++j) {
            if (!flag[j] && G[minNode][j] != INF && 
                dist[j] > dist[minNode] + G[minNode][j]) {
                dist[j] = dist[minNode] + G[minNode][j];
                p[j] = minNode;
            }
        }
    }
}

// 递归打印从源点到目标节点的路径
void printPath(int target)
{
    if (target == -1) return;
    printPath(p[target]);
    // cout << target;
    // if(target != p[target]) cout << " -> ";
    cout << target << " -> ";;
}

int main()
{
    // 初始化邻接矩阵
    fill(G[0], G[0] + N*N, INF);
    
    cout << "输入节点数n和边数m: ";
    cin >> n >> m;
    
    cout << "输入" << m << "条边(起点 终点 权重):" << endl;
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        G[u][v] = w;
        // 无向图添加反向边(如果需要)
        // G[v][u] = w;
    }
    
    int source;
    cout << "输入源点: ";
    cin >> source;
    
    dijkstra(source);
    
    cout << "\n源点 " << source << " 到各节点的最短距离:" << endl;
    for (int i = 1; i <= n; ++i) {
        cout << "节点 " << i << ": ";
        if (dist[i] == INF) {
            cout << "不可达";
        } else {
            cout << dist[i] << "\t路径: ";
            printPath(i);
        }
        cout << endl;
    }
    
    return 0;
}

测试:

// 输入节点数n和边数m: 5 7

// 输入7条边(起点 终点 权重):

// 1 2 4

// 1 3 1

// 2 3 2

// 2 4 5

// 3 2 1

// 3 4 8

// 4 5 3

// 输入源点: 1

// 源点 1 到各节点的最短距离:

// 节点 1: 0 路径: 1 ->

// 节点 2: 2 路径: 1 -> 3 -> 2 ->

// 节点 3: 1 路径: 1 -> 3 ->

// 节点 4: 7 路径: 1 -> 3 -> 2 -> 4 ->

// 节点 5: 10 路径: 1 -> 3 -> 2 -> 4 -> 5 ->

相关推荐
Sunsets_Red5 分钟前
浅谈博弈论
c++·学习·编程·博弈论·信息学竞赛·巴巴博弈
ALex_zry26 分钟前
C++26 std::inplace_vector 详解:零堆分配的定容向量
开发语言·c++
卷无止境1 小时前
C++ 类型转换变迁之路:从隐式转换到 static_cast
c++·后端
Sam09271 小时前
【AI 算法精讲 16】BPE 分词:从字节对到子词
人工智能·python·算法·ai
LJHclasstore_luo1 小时前
【题解】WebGoC 102683.大雨过后
算法·goc编程
MrZhao4001 小时前
Agent 如何持续工作:任务持久化、后台执行与定时唤醒
算法
用户701720739001 小时前
密码学之分组密码
算法
MrZhao4001 小时前
Agent 长上下文处理机制:Context Compact 与 Memory 的协同
算法
h_a_o777oah1 小时前
【动态规划】区间 DP :三层循环逻辑与模板实现细节(洛谷 P1880)
c++·算法·动态规划·acm·区间dp·化环为链·石子合并