【算法07】Dijkstra算法

对于邻接表来说,也有一个最适合的算法——Dijkstra算法。

Dijkstra算法是求解单源最短路径的算法。

什么是单源?举个例子,Floyd算法是多源的,有多个源点,能求解从任意点到任意点的距离。而Dijkstra算法是单源,的只能有一个源点,然后从这个源点(起点)出发,求解到达任意点的距离。

求解Dijkstra算法需要注意不能有负权边

这里额外提一个负环的概念:负环并不意味着环中每条边都是负权,而是说,环的总权值为负。存在负环也就没有最短路可言。(一直绕就行)

下面举个简单的例子来说明Dijkstra算法:

步骤:

  1. 先从起始点1开始,影响他周围的三个点(更新最短路)。
  2. 更新后再从这三个点中,选取一个值最小的点固定住作为新的起始点 (没有比这个值更小的可能了),然后去影响它周围的点(更新最短路)。
  3. 如此循环下去。

代码实现:

  • 每次如何选出值最小的点。——使用优先队列存储到达某个节点的最短路  堆优化
#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;

struct node{
    int now,dis;//now:当前节点 ; dis:到这个点的最短路长
    bool operator<(const node &b)const{  //STL的优先队列默认是大顶堆,重载小于号使它变成小顶堆。
        return this->dis > b.dis;
    }
};

struct edge{
    int e,v; // e:这条边的重点; v这条边的权值
};

int n,m,s,ans[100005]; 
//n:点的数量; m:边的数量; s:起点的编号; ans[N]:存储每个点的最短路

int main(){
    memset(ans,0x3F,sizeof(ans));//每个点的最短路需要被初始化成极大值
    
    cin>>n>>m>>s;
    vector<vector<edge> >edg(n+1,vector<edge>());//二维vector存储边的信息
    
    for(int i=0;i<m;i++){
        int a,b,c;  
        cin>>a>>b>>c; //输入每条边的起点、终点、权值
        edg[a].push_back((edge){b,c});
        edg[b].push_back((edge){a,c});//正反都存一遍
    }
    priority_queue<node> que;
    ans[s]=0; //到起点的最短路置为0
    que.push((node){s,0});//把起点压入队列
    while(!que.empty()){
        node temp=que.top();//每次拿出堆顶元素
        que.pop();
        if(temp.dis>ans[temp.now]){ //做一个剪枝,如果堆顶元素已经被固定过,就直接跳过
            continue;
        }
        for(int i=0;i<edg[temp.now].size();i++){//遍历以它为起点的每条边
            int e=edg[temp.now][i].e,v=edg[temp.now][i].v;
            if(ans[e]>ans[temp.now]+v){ //更新相邻节点的最短路
                ans[e]=ans[temp.now]+v;
                que.push((node){e,ans[e]});//更新之后的节点压入队列
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(ans[i]==0x3F3F3F3F){//如果还是极大值,说明没有到这个点的最短路
            cout<<-1<<endl;
        }else{
            cout<<ans[i]<<endl;//输出最短路
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值