最短路径问题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 ->

相关推荐
吴可可1233 分钟前
AutoCAD2016二次开发环境配置指南
算法·机器学习
一条大祥脚6 分钟前
ABC461 枚举|扫描线|动态前缀和|数论|dfs枚举子集
算法·深度优先
计算机安禾9 分钟前
【数据库系统原理】第14篇:关系模式的语义约束:函数依赖的公理系统与闭包计算
人工智能·算法·机器学习
MZZ骏马10 分钟前
C++ 极简模式的日志
c++
量化君也11 分钟前
快速入门量化交易都要学些什么?
大数据·人工智能·python·算法·金融
AbandonForce21 分钟前
滑动窗口:定长滑动窗口与不定长滑动窗口
数据结构·c++·算法
炸薯条!34 分钟前
二叉树的链式表示(2)
java·数据结构·算法
Tairitsu_H37 分钟前
[LC优选算法#2] 滑动窗口 | 长度最小的子数组 | 无重复字符的最长子串 | 最大连续1的个数
算法
小欣加油38 分钟前
leetcode3689最大子数组总值I
c++·算法·leetcode·职场和发展·贪心算法
下午写HelloWorld1 小时前
【概念与应用】轻量级加密算法LEA、动态脱敏算法DDA、零知识证明ZKP和优化协同交互协议OCIP
算法·区块链·密码学·安全架构·零知识证明