图论之最短路径算法

在这里插入图片描述

Dijkstra 算法

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。
是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。

算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止

朴素Dijkstra算法

https://www.acwing.com/problem/content/851/

在这里插入图片描述

变量解释

变量名 含义
int g[N][N] g[i][j] i->j 的距离
int dis[N] dis[i] 1->i 的最短距离
bool vis[N] vis[i] 当前1->i 的最短距离是否确定
int n,m n个点,m条边

算法流程

  1. 初始化最短距离。起点到自己的最短距离为0,到其他点的距离为INF(0x3f3f3f3f)
  2. 找距离起点最近的点。在没有确定最短距离的点中,找到距离起点距离最近的一个点
  3. 标记。将2找到的最近的点标记为已经确定最短路径的点
  4. 松弛。用2找到的点,更新从起点到每一个点的最短距离,min(dis[j], dis[t] + g[t][j])
  5. 重复第2步,直至所有存在最短路径的点更新完毕
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510;
int g[N][N];        // g[i][j]  i->j 的距离
int dis[N];         // dis[i]   1->i 的最短距离
bool vis[N];        // vis[i]   当前1->i 的最短距离是否确定
int n,m;

int dijkstra() {
   
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    
    for(int i = 0; i < n; i++) {
           // 访问 n 次
        int t = -1;
        for(int j = 1; j <= n; j++) {
      // 访问 n 个点
            if(!vis[j] && (t == -1 || dis[t] > dis[j]))
                t = j;
        }
        
        vis[t] = true;
        for(int j = 1; j <= n; j++)      //依次更新每个点所到相邻的点路径值
            dis[j] = min(dis[j], dis[t] + g[t][j]);
    }
    
     //如果第n个点路径为无穷大,不存在 1->n 最短路径
    if(dis[n] == 0x3f3f3f3f) return -1;
    return dis[n];
}

int main() {
   
    cin >> n >> m;
    memset(g, 0x3f, sizeof g);
    for(int i = 0; i < m ; i++) {
   
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        g[x][y] = min(g[x][y],z);   //如果发生重边的情况则保留最短的一条边
    }
    
    cout << dijkstra() << endl;;
    return 0;
}

堆优化版本

https://www.acwing.com/problem/content/852/

在这里插入图片描述

分析

为什么说是用堆进行优化,因为在算法的第一步中,需要在没有确定最短路径的点中,找到从起点到该点距离最短的一个点。

如果不加优化,这一步是O(n)的时间复杂度,但是如果使用小根堆的话,就可以优化为O(1),随后的一个pop操作,这是O(log n)的。整体来看,第一步可以优化为O(log n)

随后,在进行松弛的过程中,最多有m条边,而入队操作的时间复杂度为O(log n),总体时间复杂度为O(m log n)

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <queue><
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值