搜索与图论之最短路算法

本文详细介绍了四种常见的最短路算法:朴素Dijkstra算法、堆优化Dijkstra算法、Bellman-Ford算法以及SPFA算法。针对每种算法,文章不仅提供了实现代码,还分析了其适用场景和优化策略。此外,还讨论了Dijkstra算法处理负权边的问题,并阐述了如何通过SPFA算法判断图中是否存在负环。最后,探讨了Floyd算法的思想及应用。
摘要由CSDN通过智能技术生成

目录

 1.朴素dijkstra算法

2.堆优化版dijkstra算法

        分析

为什么Dijkstra不能处理(有些)负权边

负权概念

3.Bellman-Ford算法

几点说明:

2.memcpy()函数:memcpy_百度百科,memcpy函数效率很高

4(1)spfa算法求最短路

几点说明:

1.spfa算法实际上是对Bellman-Ford算法的一个优化

2.区分顶点和顶点下标

3.如何判断最短路是否存在

解释:st数组的作用

4(2)spfa算法判断负环

原理:对于一个n个点,m条边的有向图,如果到达一个点的最短路大于等于n,那么就一定存在负环。

几点说明:

5.Flord算法

思想(动态规划)

说明:


 1.朴素dijkstra算法

例题引入:849. Dijkstra求最短路 I - AcWing题库

 

AC代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 510;

int g[N][N], dist[N];   //邻接矩阵,到节点i的最短路径    
bool st[N]; //判断i点是否已经找到了最短路径
int n, m;

void djikstra()
{
    memset(dist, 0x3f, sizeof dist);    //初始化所有最短路径为最大值
    
    dist[1] = 0;    //以节点1为起点,所以到节点1的最短距离为0
    
    for(int u = 1; u <= n; u ++ )   
    {        
        int k = -1;
        for(int i = 1; i <= n; i ++ )   //找到当前状态下所有最短路径中最短的一个
            if((k == -1 || dist[k] > dist[i]) && !st[i])  
                k = i;
        
        
        st[k] = true;   //标记这个节点的最短路径已经找到了
        
        for(int i = 1; i <= n; i ++ )   //用这个最短路径更新所有最短路径
            dist[i] = min(dist[i], dist[k] + g[k][i]);
        
    }
}


int main()
{
    scanf("%d%d", &n, &m);
    
    memset(g, 0x3f, sizeof g);  
    
    for(int i = 0; i < m; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = min(g[a][b], c);  //通过这一步可以忽略掉重边并取得最小的边值
    }
    
    djikstra();
    
    
    if(dist[n] == 0x3f3f3f3f)   puts("-1"); //要判断是否有路
    else    cout << dist[n] << endl;
    
    return 0;
}


2.堆优化版dijkstra算法

例题引入:850. Dijkstra求最短路 II - AcWing题库

AC代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

typedef pair<int, int> PII;
const int N = 160010;

int h[N], ne[N], e[N], w[N], idx;
int dist[N];
bool st[N];
int n, m;

void add(int a, int b, int c)
{
    w[idx] = c; //在赋权值的时候,要在idx++之前!
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void djikstra()
{
    memset(dist, 0x3f, sizeof dist);
    
    priority_queue<PII, vector<PII>, greater<PII> > heap;
    heap.push({0,1});   //(距离,下标号)
    
    dist[1] = 0;
    
    while(heap.size())  //队列不为空
    {
        auto t = heap.top();    //取队头
        heap.pop();     //出队
        
        int ver = t.second, distance = t.first;
        if(st[ver]) continue;   //如果这个点已经确定了最短路径
        
        st[ver] = true; //标记一下
        
        for(int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];   //我们在邻接表存的是下标,通过e[i]得到顶点
            if(dist[j] > distance + w[i])   //如果路径可以被优化
            {
                dist[j] = distance + w[i];  //更新路径
                heap.push({dist[j], j});    //假如队列
            }
        }
    }
}


int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);
    
    while(m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    
    djikstra();
    
    if(dist[n] == 0x3f3f3f3f)   puts("-1");
    else    cout << dist[n] << endl;
    
    
    return 0;
}

分析

  1. 使用优先级队列
    在使用前需要先搞清楚优先级队列的时间复杂度,往一个长度为n的优先级队列中插入一个元素时间复杂度是O(logn)。那么把n个元素插入一个空的优先级队列中时间复杂度就为O(nlogn)。
  2. O(Log n)

    如果在一个大小为n的循环中,循环变量是指数级的递增或递减,这个循环的复杂度就为O(Log n).
  3. 堆优化,主要优化的是找当前状态下的最短的路径的时间,在朴素算法中,是通过一个for循环完成,时间复杂度是线性的,但是在堆优化版本中,使用优先队列可以在常数时间内取出这个最短的路径,即队头,不过需要注意的是维护优先队列的时间复杂度是为O(N)的
  4. 优先队列的实现方式通过一个pair,储存最短路和下标


为什么Dijkstra不能处理(有些)负权边

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值