最短路径算法总结(floyd,dijkstra,bellman-ford)

继续复习数据结构和算法,总结一下求解最短路径的一些算法。

弗洛伊德(floyd)算法

弗洛伊德算法是最容易理解的最短路径算法,可以求图中任意两点间的最短距离,但时间复杂度高达\(O(n^3)\),主要思想就是如果想缩短从一个点到另一个点的距离,就必须借助一个中间点进行中转,比如A点到B点借助C点中转的话AB的距离就可以更新为\(D(a,b)=Min(D(a,b),D(a,c)+D(c,b))\),这样我们用每一个结点作为中转结点,尝试对另每两个结点进行距离更新,总共需要三层循环进行遍历。

核心代码如下,图存储在邻接矩阵G中。

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            for (int k = 0; k < n; k++)
            {
                G[j][k] = min(G[j][k], G[j][i] + G[i][k]);
            }
        }
    }

迪杰斯特拉(Dijkstra)算法

迪杰斯特拉算法是一种求解单源最短路径的算法,给定一个结点,可以求出图上各个结点到该结点最短距离。

没学过的话推荐看看这个视频:https://www.bilibili.com/video/av21376839?p=13,从8分钟开始。看完之后基本上就明白了Dijkstra算法的运行过程。总结一下就是不断寻找离源点最近的点并将其作为新的源点去更新其他点到目标点的距离。

代码如下,nowIndex代表当前源点编号,minDis是当前源点到其他点的最短距离,用于选择下一个源点,dis数组存储每个点到最终目标点的距离,也就是结果,mark数组用于标记结点是否被当作源点过。

#include<iostream>
#include<algorithm>
using namespace std;

#define inf 100000000

int G[10][10];
int dis[10];
bool mark[10];
int n, m;

void dijkstra(int nowIndex)
{
    mark[nowIndex] = true;
    for (int i = 1; i <= n; i++)//先将跟源点直接相连的结点更新一遍
        dis[i] = min(dis[i], G[nowIndex][i]);
    for (int i = 1; i < n; i++)//循环n-1次,因为源点已经更新过了
    {
        int minDis = inf;
        for (int j = 1; j <= n; j++)//找离当前源点最近的点
        {
            if (!mark[j] && dis[j] < minDis)
            {
                minDis = dis[j];
                nowIndex = j;
            }
        }
        mark[nowIndex] = true;
        for (int j = 1; j <= n; j++)//用当前源点去更新
            dis[j] = min(dis[j], dis[nowIndex] + G[nowIndex][j]);
    }
}

int main()
{
    cin >> n >> m;//输入顶点数和边数
    int u, v, w;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i != j)
                G[i][j] = inf;
            else
                G[i][j] = 0;
    for (int i = 1; i <= n; i++)
        dis[i] = inf;
    for (int i = 0; i < m; i++)
    {
        cin >> u >> v >> w;//输入无向边
        G[u][v] = w;
        G[v][u] = w;
    }
    dijkstra(1);//以1号结点为源点
    for (int i = 1; i <= n; i++)
    {
        cout << dis[i] << ' ';
    }

    return 0;
}

邻接表实现

使用邻接表存储图能大大降低空间复杂度,代码如下:

#include<iostream>
#include<algorithm>
using namespace std;

#define inf 100000000
#define maxN 10000

int value[maxN], to[maxN], nextL[maxN];
int head[maxN], total;
int dis[maxN];
bool mark[maxN];
int n, m;

void dijkstra(int nowIndex)
{
    for (int i = 0; i <= n; i++)dis[i] = inf;
    dis[nowIndex] = 0;
    mark[nowIndex] = true;
    for (int i = head[nowIndex]; i; i = nextL[i])
    {
        dis[to[i]] = min(dis[to[i]], dis[nowIndex] + value[i]);
    }
    for (int i = 1; i < n; i++)//循环n-1次,因为源点已经更新过了
    {
        int minDis = inf;
        for (int j = 1; j <= n; j++)//找离当前源点最近的点
        {
            if (!mark[j] && dis[j] < minDis)
            {
                minDis = dis[j];
                nowIndex = j;
            }
        }
        mark[nowIndex] = true;
        for (int j = head[nowIndex]; j; j = nextL[j])
        {
            dis[to[j]] = min(dis[to[j]], dis[nowIndex] + value[j]);
        }
    }
}

void AddLine(int a, int b, int c)
{
    total++;
    to[total] = b;
    value[total] = c;
    nextL[total] = head[a];
    head[a] = total;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        AddLine(a, b, c);
    }
    dijkstra(1);
    for (int i = 1; i <= n; i++)
        cout << dis[i] << " ";

    return 0;
}

堆优化

普通的Dijkstra时间复杂度为\(O(n^2)\),但可以通过优化达到\(O(nlogn)\),注意在上面的循环中我们每次都要取出离当前源点最近的点,所以可以用优先级队列来优化。每次搜索将修改过dis的点进队,然后每次取队首就是最近的点。

代码如下:

#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

#define inf 100000000
#define maxN 10010

int s;
int value[500001], to[500001], nextL[500001];
int head[maxN], total;
int dis[maxN];
bool mark[maxN];
int n, m;

typedef pair<int, int> disID;
priority_queue<disID,vector<disID>,greater<disID>> q;

void dijkstra(int nowIndex)
{
    for (int i = 0; i <= n; i++)dis[i] = inf;
    dis[nowIndex] = 0;
    q.push(disID(0, nowIndex));
    while (!q.empty())
    {
        int t = q.top().second;
        q.pop();
        if (mark[t])continue;
        mark[t] = true;
        for (int i = head[t]; i ; i=nextL[i])
        {
            if (dis[to[i]] > dis[t] + value[i])
            {
                dis[to[i]] = dis[t] + value[i];
                q.push(disID(dis[to[i]], to[i]));
            }
        }
    }
}

void AddLine(int a, int b, int c)
{
    total++;
    to[total] = b;
    value[total] = c;
    nextL[total] = head[a];
    head[a] = total;
}

int main()
{
    cin >> n >> m >> s;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        AddLine(a, b, c);
    }
    dijkstra(s);
    for (int i = 1; i <= n; i++)
    {
        cout << dis[i] << ' ';
    }

    return 0;
}

Bellman-ford算法

上面的Dijkstra算法存在一个问题就是不能处理存在负权边的情况,只要有边的权值是负数就不能用,这时可以用Bellman-ford算法解决。

Bellman-ford算法的思想是这样的,我们将每条边的起点、权值、终点存储为三个数组from[i],val[i],to[i],然后扫描每一条边,看能不能通过走这条边来使dis[to[i]]减少。

代码很简单如下:

#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

#define inf 100000000
int from[10000], val[10000], to[10000];
int dis[10000];
int n, m;

void Bellman_ford(int u)
{
    for (int i = 0; i <= n; i++)dis[i] = inf;
    dis[u] = 0;
    while (true)
    {
        bool update = false;
        for (int i = 1; i <= m; i++)
        {
            if (dis[from[i]] != inf && dis[to[i]] > dis[from[i]] + val[i])
            {
                dis[to[i]] = dis[from[i]] + val[i];//更新
                update = true;
            }
        }
        if (!update)break;//直到每一条边都不能使dis减少
    }
}

int main()
{
    cin >> n >> m ;
    for (int i = 1; i <= m; i++)
    {
        cin >> from[i] >> to[i] >> val[i];
    }
    Bellman_ford(1);
    for (int i = 1; i <= n; i++)
        cout << dis[i] << ' ';
    return 0;
}

算法中的while循环最多循环n-1次,所以Bellman-ford的时间复杂度是\(O(mn)\),不仅能处理负权边,而且在稀疏图(顶点数远多于边数)当中比Dijkstra快。

原文地址:https://www.cnblogs.com/LiveForGame/p/12289324.html

时间: 2024-11-05 18:35:31

最短路径算法总结(floyd,dijkstra,bellman-ford)的相关文章

最短路径算法之一——Floyd算法

Floyd算法 Floyd算法可以用来解决任意两个顶点之间的最短路径问题. 核心公式为: Edge[i][j]=Min{Edge[i][j],Edge[i][k]+Edge[k][j]}. 即通过对i,j两个顶点之间插入顶点后比较路径的大小来进行松弛. 首先我们来定义一个二维数组Edge[MAXN][MAXN]来存储图的信息. 这个图的Edge数组初始化以后为 相当于任意两点之间不允许经过其他点时的距离情况. Code1: 1 //经过1号顶点 2 for(i=1;i<=n;i++) 3 for

最短路径算法之二——Dijkstra算法

Dijkstra算法 Dijkstra算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 注意该算法要求图中不存在负权边. 首先我们来定义一个二维数组Edge[MAXN][MAXN]来存储图的信息. 这个图的Edge数组初始化以后为 我们还需要用一个一维数组dis来存储1号顶点到其余各个顶点的初始路程,如下. 这个dis数组中存的是最短路的估计值. 通过Dijkstra算法来松弛后,dis存的为从初始点到各点的精确值(最短路径)了. Dijkstra算法实现如下(以HDU1548为例

所有顶点之间的最短路径算法:Floyd算法。

Floyd算法的基本思想是:设集合S的初始状态为空,然后依次向集合S中加入顶点 0,1,...,n-1,每次加入一个顶点,用二维数组d保存各条最短路径的长度,其中d[i][j]存放的是顶点i到顶点j的最短路径的长度. 详细的说明: Floyd算法中最重要的办法为二维数组d[i][j],d[i][j]为从i到j中间只经过S中的顶点的.所有可能的路径中的最短路径的长度.如果从i到j通过S中的节点无法联通,则设置d[i][j]为正无穷.可以称d[i][j]存放的是从i到j的 当前最短路径 的长度.而随

单元最短路径算法模板汇总(Dijkstra, BF,SPFA),附链式前向星模板

一:dijkstra算法时间复杂度,用优先级队列优化的话,O((M+N)logN)求单源最短路径,要求所有边的权值非负.若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的. 设road[i][j]表示相邻的i到j的路长U集合存储已经求得的到源点最短路径的节点,S集合表示还没求得的节点dis[i]表示i到源节点(设为0)的最短路径vis[i]=1表示i节点在U集合中 刚开始dis[0]=0,vis[0]=1;dis[i]=maxn,vis[i]=0;for 1 to

最短路径算法集锦

/* Name: 最短路径算法集锦 Copyright: Author: 巧若拙 Date: 12/11/14 15:32 Description: 列举了深度优先搜索的递归和非递归算法,Dijkstra最短路径算法, 基于Bellman-Fort最短路径算法的改进型广度优先搜索算法, Floyd-Warshall最短路径算法的原始版和变化版 本文是阅读<啊哈!算法>后的学习笔记,代码与教材中有些差异,若有错误请指正,谢谢! 测试数据: 5 7 0 3 2 0 4 9 4 2 1 4 1 3

单源最短路径算法

目录 基本性质 Bellman Ford算法 spfa(Shortest Path Faster Algorithm) 算法 Dijkstra 算法 例题练习 主要参考算法导论 基本性质 使用min_w(s,v)表示源节点s到v的最短路径长度: w(u,v)表示节点u到v的权重: u.d表示源节点s到节点u的当前路径长度: 松弛操作 relax(u,v,w) { if(u.d + w < v.d) { v.d = u.d + w; } } 三角不等式 min_w(s,v) <= min_w(s

最短路径算法实现

最短路径算法 1.Dijkstra算法 目的是求解固定起点分别到其余各点的最短路径 步骤如下: 准备工作:构建二位矩阵edge,edge[i][j]存储i->j的权重,如果i==j则edge[i][j]=0,如果i和j不是直达的,则edge[i][j]=MAX_INT 构建数组dis[],其中dis[i]表示其实点start->i之间的权重,不断更新,得到最小的权重 选取离start最近的直达点,(注,非直达的点一定会经过中间的跳变点,间接到达,首先考虑的一定是经过离start最近的点进行跳变

数据结构与算法分析(五)——最短路径算法

0) 引论 正如名字所言,最短路径算法就是为了找到一个图中,某一个点到其他点的最短路径或者是距离. 最短路径算法一般分为四种情况: a) 无权重的最短路径 b) 有权重的最短路径 c) 边的权重为负的图 d) 无圈的图 ps:上面的情况针对的都是有向图. 1) 无权重的最短路径 下图是一个例子:假设我们取点v3作为初始点,计算点v3到图中所有点的路径以及距离(包括点v3). a) v3到v3的路径长为0. b) 沿着v3的邻接点查找,找到v1,那么v3到v1的路径长为1:找到v6,那么v3到v6

Bellman - Ford 算法解决最短路径问题

Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力不从心了,而Bellman - Ford算法可以解决这种问题. Bellman - Ford 算法可以处理路径权值为负数时的单源最短路径问题.设想可以从图中找到一个环路且这个环路中所有路径的权值之和为负.那么通过这个环路,环路中任意两点的最短路径就可以无穷小下去.如果不处理这个负环路,程序就会永远运