复习之路~最短路算法Dijsktra

前言

本文所有内容出自于个人的理解,可能有谬误,还请多多包涵,敬请指正。qq :2039316792
本文中参考的所有文章,将会在专页中一一列出。

目录

  1. Dijkstra
  2. Spfa
  3. Floyd

正文

1、Dijkstra

概论

Dijkstra算法是一种单源最短路算法,适用于无负权的图。其采用了BFS+贪心的方式来更新最短路。

基本流程

  1. 初始化dis[每一个点]= ∞,dis[起点]=0。
  2. 标记起点,加入集合S。其他点不标记,加入集合U。
  3. 从集合U中选取与当前点A距离最近的点B。
  4. 若起点到点A的距离+点A到点B的距离小于dis[]中存贮的值,则执行语句4,否则若起点到点A的距离+点A到点B的距离小于dis[]中存贮的值,则更新dis和当前点。
  5. 若所有的点都已经被标记过,则算法结束,否则,重复执行语句3。

PS :由于时间原因,不在此加图,以后我会更新一个GIF来更好得描述次过程。

时间复杂度

  • 朴素算法: O( n² )

  • heap :

  1. 小根堆(优先队列) O( (n+m)logn )
  2. 斐波那契堆 O( m+nlogn )

代码实现

PS:以下代码以洛谷 P3371 【模板】单源最短路径(弱化版)为例

#include<bits/stdc++.h>
#define maxn 10005
#define inf 2147483647
using namespace std;
int n,m,s,minn,s,cnt;
int dis[maxn],head[maxn];
bool vis[maxn];
struct edge
{
    int nxt,to,w;
}h[500005];
void add(int u,int v,int w)
{
    h[++cnt].next=head[u];
    h[tot].to=v;
    h[tot].w=w;
    head[u]=cnt;
}
inline void dij() 
{
    for(int i=1;i<=n;i++)dis[i]=inf;
    dis[s]=0;
    for(int i=1;i<=n;i++)
    {
        minn=inf;t=-1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]<minn)
            {
                minn=dis[j];
                t=j;
            }
        }
        if(t==-1)break;
        vis[t]=1;
        for(int j=head[t];j;j=h[j].next)
        {
            if(!vis[h[j].to]&&dis[h[j].to]>(h[j].w+dis[t]))
            dis[h[j].to]=h[j].w+dis[t];
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    int u,v,w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    dij();
    for(int i=1;i<=n;i++)
    printf("%d",dis[i]);
    return 0;
}

优化

在上述代码中,我们用枚举的方式来找到与当前节点相连的非来源边的边的最小值,这里显然是十分浪费时间的,作为追求时间效率oiers,我们对这里用堆来优化。

一般而言,oiers还会追求代码的简洁,所以,我们用stl中的priority_queue来模拟堆。

PS:以下代码以洛谷 P3371 【模板】单源最短路径(标准版)为例

#include <bits/stdc++.h>
#define inf 1e9f
#define ll long long
#define maxn 200100
using namespace std;
struct node
{
    ll val,id;
    bool operator <(const node &x) const 
    {
        return val>x.val;
    }
};
priority_queue<node> q;
vector< pair<ll,ll> >a[maxn];
ll n,m,s,dis[maxn];
bool vis[maxn]; 
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        a[u].push_back(make_pair(v,w));
    }
    for(int i=1;i<=n;i++)dis[i]=inf;
    dis[s]=0;
    q.push((node){0,s});
    while(!q.empty())
    {
        ll p=q.top().id;
        q.pop();
        if(vis[p])continue;
        vis[p]=true;
        for(int i=0;i<a[p].size();i++)
        {
            int to=a[p][i].first,val=a[p][i].second;
            if(!vis[to]&&dis[p]+val<dis[to])
            {
                dis[to]=dis[p]+val;
                q.push((node){dis[to],to}); 
            }
        }
    }
    for(int i=1;i<=n;i++) printf("%lld ",dis[i]);
    return 0;
}

2、Spfa

3、floyd

参考文献

  1. 为什么Dijkstra算法不适用边长为负数的情况
  2. spfa的SLF 和 LLL优化算法

福利

在这里插入图片描述
就不更╭(╯^╰)╮

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值