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

  • 但dist[w] ≥ dist[v](因为v

是最小值),矛盾!

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

```math

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

```

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

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

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

  • 路径重构:递归回溯 `p[j] → p[v] → ... → 源点`

代码示例:

输入图中节点个数和节点之间的边数,然后根据提示输入不同节点之间每个边的起点和终点以及两点之间的距离,然后输入源点,使用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 ->

相关推荐
式5162 小时前
CUDA编程学习(五)线程模型定义、矩阵相加
学习·算法·矩阵
C蔡博士2 小时前
大数乘法的算法演进:从小学方法到 Karatsuba
算法·大数乘法·分治思想
REDcker2 小时前
glibc、libstdc++ 与 libc++ 区别与联系
开发语言·c++
2401_844221322 小时前
内存对齐与缓存友好设计
开发语言·c++·算法
橘颂TA2 小时前
【笔试】算法的暴力美学——牛客 NC221681:dd爱框框
算法
天天进步20152 小时前
WrenAI 深度解析:算法视角:wren-ai-service 如何利用 RAG 与 Metadata 提升 SQL 准确率?
人工智能·sql·算法
一叶落4382 小时前
36. 有效的数独(Valid Sudoku)题解(C语言)
c语言·数据结构·算法·leetcode·哈希算法
qiuyunoqy2 小时前
Linux进程 --- 5(进程地址空间初识)
linux·c++·算法
Sakinol#2 小时前
Leetcode Hot 100 ——贪心算法
算法·leetcode·贪心算法