最短路径算法(Bellman-Ford/Dijkstra/Floyd)


最短路径算法

最短路径算法

Bellman-Ford(单源最短)

算法核心

  • 对于每一个边都看能不能去更新单源最短路径
  • 在更新过程中并不确定哪一个边就是最短路径,而是始终抱着试探的心态去更新最短路径
  • 一旦某一次起点到其他结点的最短路径不更新时,就表示全部为最短路径
负环的判断

Bellman-Ford算法可以判断图中是否有负环,当出现负环时是不存在最短路的,并且易知最多通过(V-1)条路就能到达任何一个点并且距离最短,因此如果在第V次的循环仍能更新最短路径表,则代表图中存在负环

算法代码

const int MAX_E=10000;
const int MAX_V=10000;
const int INF=99999;

struct edge
{
    int from,to,cost;
}es[MAX_E]; //记录边的数组

int dist[MAX_V];    //最短路径表
int V,E;    //顶点数和边数

//从起点s出发到其它点的最短路径,true表示有负环,false表示无负环
bool bellman_ford(int s)
{
    for(int i=0;i<V;i++) dist[i]=INF;
    
    dist[s]=0;  //s->s的最短距离为0
    while(true){
        bool update=false;
        
        for(int i=0;i<E;i++){
            edge e=es[i];
            if(dist[e.from]<dist[e.to]+e.cost){ //对每个边都看,是否能够通过这个边以更小的代价到达某个点
                dist[e.from]=dist[e.to]+e.cost;
                update=true;
            }
        }
        
        if(!update) break;  //一旦有一次不再更新,就求解出来最短路径了
    }
    
    bool fuhuan=false;
    for(int i=0;i<E;i++){
        edge e=es[i];
        if(dist[e.from]<dist[e.to]+e.cost){ //对每个边都看,是否能够通过这个边以更小的代价到达某个点
            dist[e.from]=dist[e.to]+e.cost;
            fuhuan=true;
        }
    }
    
    return fuhuan;
}

算法分析

算法的时间复杂度为O(|V|*|E|)

Dijkstra算法(单源)

算法核心

  • 在最短路径表中每次确定一个不可改变的最短路径,以此为基准依次更新最短路径表
无法判断存在负权边的图

因为每次都确定了一个不可改变的最短路径,因此存在一定的局部片面性

例如:

在这里插入图片描述

最后结果的最短路径表:

在这里插入图片描述

实际的最短路径表:

在这里插入图片描述

算法代码

const int MAX_V=10000;
const int INF=99999;

int cost[MAX_V][MAX_V]; //cost[u][v]表示边e=(u,v)的权值,不存在时设置为INF
int dist[MAX_V];   //顶点s出发的最短距离
bool used[MAX_V];   //已经使用过的点
int V;  //顶点数

void dijkstra(int s)
{
    fill(dist,dist+V,INF);
    fill(used,used+V,false);
    dist[s]=0;
    
    while(true){
        int v=-1;
        for(int i=0;i<V;i++){
            if(!used[i]&&(v==-1||dist[i]<dist[v])) v=i;
        }
        
        if(v==-1) break;
        used[v]=true;
        
        for(int i=0;i<V;i++)
            dist[i]=min(dist[i],dist[v]+cost[v][i]);
    }
}

算法分析

算法的复杂度为O(|V|2)

堆优化的Dijkstra算法(单源)

算法核心

  • 每次都将更新过的边的最短路径和顶点编号压入最小堆中,直到堆为空

算法代码

const int INF=99999;
const int MAX_V=100000;
struct edge
{
    int to,cost;
};

typedef pair<int , int> P;  //first是最短距离,second是顶点的编号

int V;  //顶点数
vector<edge> G[MAX_V];  //邻接表存储
int dist[MAX_V];    //最短路径表

void dijkstra(int s)
{
    //最小堆
    priority_queue<P,vector<P>,greater<P>> que;
    
    fill(dist,dist+V,INF);
    dist[s]=0;  //s到s的最短距离为0
    que.push(P(0,s));   //将s到s的最短距离压入堆中
    
    while(!que.empty()){
        P p=que.top(); que.pop();
        int v=p.second; //v为该最短路的结点
        if(dist[v]<p.first) continue;   //若最短路表中记录的最短路与弹出小,则证明到该结点的最短路是旧纪录
        //遍历结点v的邻接表,更新相邻结点的最短路径
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(dist[e.to]>dist[v]+e.cost){
                dist[e.to]=dist[v]+e.cost;
                que.push(P(dist[e.to],e.to));
            }
        }
    }
}

Floyd算法(多源)

算法核心

  • 任意两个点的最短路径,都认为能够通过某个中间点进行中转之后达到最短路径
可判断负环
  • 当某个结点到自身的距离为负数时,即存在负环

算法代码

const int MAX_V=100000;
int d[MAX_V][MAX_V];    //邻接矩阵,在最终为多源最短路径表
int V;  //顶点数

void floyd()
{
    for(int k=0;k<V;k++)
        for(int i=0;i<V;i++)
            for(int j=0;j<V;j++)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}

算法分析

算法的时间复杂度为O(|V|3)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

省下洗发水钱买书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值