最短路径——迪杰斯特拉算法

这篇具有很好参考价值的文章主要介绍了最短路径——迪杰斯特拉算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

案例引入

迪杰斯特拉,C++,数据结构与算法,数据结构,图论,Powered by 金山文档

日常生活中常常涉及最短路径问题,如在一个城市交通网中,如何选取从起点到达终点的路径,才能使这一趟旅程的路程最短?或所需时间最少?或所需交通费用最低?诸如此类问题都可以抽象为求解图的最短路径问题。我们把图的顶点表示为城市的交通站点边表示交通站点间的路径边的权值表示为交通站点间的路径的距离、所需时间或费用等等,则上述问题放映到图上就是,如何求解图顶点间权值之和最小的路径

由于城市交通站点间具有方向性,故常选取带权值的有向网表示城市交通网。习惯上,在有向网的一条路径中,第一个顶点被称为源点,最后一个顶点被称为终点

常见的最短路径问题分为两类:单源点到达其余顶点的最短路径以及任意一对顶点间的最短路径

求解单源点到其余顶点的最短路径问题

迪杰斯特拉算法(DIJ)思路

常用于求解单源点到其余顶点的最短路径问题的DIJ算法主要采用了源点借助中转点能获取到达终点更短路径的方法,逐步将其余顶点加入最短路径终点集。

如图所示,A借助B作为中转点获得到达C的更短路径:

迪杰斯特拉,C++,数据结构与算法,数据结构,图论,Powered by 金山文档

算法的求解过程

对于有向网N=(V,E)而言,欲求顶点v(迪杰斯特拉,C++,数据结构与算法,数据结构,图论,Powered by 金山文档)到达N的其余顶点 V-{v}的最短路径,可以将N的顶点分为两部分S与V-S,S用于存放已经确定最短路径的终点,V-S用于存放没有确定为最短路径的顶点。每一次S获取新成员前都需要挑选权值最小的<v,t>,将t加入到S中,同时源点v将利用最新最短路径终点t作为中转点更新源点v到达其余在V-S中顶点的最短路径,直到S==V

注意:假如出现两条最短路径相同的情况,则随意选取其中一条。

在上述过程中,我们可以保证每一次加入的最短路径终点u都是正确的,且后续加入的终点t无法被源点v借用以更新源点到终点u的更小路径权值。这是因为,假设dis(v,u)表示从顶点v到顶点u的权值,那么由于终点u比终点t提前进入最短路径终点集S,则dis(v,u)>=dis(v,t),那么dis(v,t)+dis(t,u)>dis(v,u)必定无法成立。除非dis(t,u)<0,存在负权边,但这不在我们的考虑范围之内(城市中的距离或一段旅程的花销一般情况下不会出现现负值)。

数据结构说明

本例采取邻接矩阵作为有向网的存储结构,其中Vertype表示顶点的数据类型ArcType表示边的权值Mvnum表示邻接矩阵的最大顶点数目。在为邻接矩阵初始化过程中,假如两顶点间不存在路径,那么G.arcs[i][j]=inf(inf是一个较大数,表示无穷大)。

辅助数组说明

第一,我们需要bool S[Mvnum]用以记录网中顶点是否已经进入到最短路径终点集,且初始化为S[v]=true,其余顶点S[i]=false,此过程将在Init函数中进行。

第二,我们需要int shortestDis[Mvnum]用以记录当前源点到各个终点的最短路径。在初始化时,该数组shortestDis[i]表示源点到下标为i的顶点间的路径权值,初始化同样是在Init函数中进行。

第三,我们需要int Path[Mvnum]用于记录,在当前源点到各个顶点的最短路径<v,vi>上,顶点vi的直接前驱,方便获取最短路径上经历过的顶点顺序。在初始化时,假如<v,vi>存在路径,则Path[vi]=v,否则Path[vi]=-1.

算法的代码实现

AMGraph的实现

//文件名为:AMGRraph.h
#pragma once
#include<iostream>
using namespace std;

//邻接矩阵存储有向网
#define Mvnum 100  //最大顶点数
typedef char VerType;  //顶点数据类型
typedef int ArcType;  //弧的权值
typedef struct AMGraph {
    int vexnum, arcnum;
    VerType vexs[Mvnum];          //顶点信息表
    ArcType arcs[Mvnum][Mvnum];   //邻接矩阵
}AMGraph;

//创建有向网
void CreateUDN(AMGraph& G);
//文件名为:AMGraph.cpp
#include"AMGraph.h"
//用邻接矩阵创建有向网
void CreateUDN(AMGraph& G)
{
    //inf表示两顶点间没有线路,距离为无穷大
    int inf = 99999999;
    //输入顶点与弧的个数
    cin >> G.vexnum >> G.arcnum;
    //判断合法性
    if (G.vexnum > Mvnum || G.arcnum > (G.vexnum - 1) * G.vexnum)
    {
        cout << "所输入信息非法" << endl;
        return;
    }
    //输入顶点的信息,类型为char
    for (int i = 0;i < G.vexnum;i++)
    {
        cin >> G.vexs[i];
    }
    //将图的边初始化
    for (int i = 0;i < G.vexnum;i++)
    {
        for (int j = 0;j < G.vexnum;j++)
        {
            if (i != j)
                G.arcs[i][j] = inf;
            else
                G.arcs[i][j] = 0;
        }
    }
    //输入权值
    for (int i = 0;i < G.arcnum;i++)
    {
        //输入v1,v2作为弧<v1,v2>的顶点以及该弧权值w
        //顶点下标从0开始
        int v1, v2, w;
        //此处省略了查找v1,v2编号的过程
        cin >> v1 >> v2 >> w;
        //判断合法性
        if (v1 == v2 || v1 >= G.vexnum || v2 >= G.vexnum
            || v1 < 0 || v2 < 0)
        {
            i--;
            continue;
        }
        if (G.arcs[v1][v2] != inf)
        {
            i--;
            continue;
        }
        //输入弧<v1,v2>的权值
        G.arcs[v1][v2] = w;
    }
    //创建完毕
}


DIJ算法实现

//文件名为:DIJ.h
#pragma once
#include"AMGraph.h"

/*
    迪杰斯特拉算法利用了“中转”点不断更新最短路径,
    直到求出源点到顶点的所有最短路径

    我们知道,又是从某一个顶点到达另外一个顶点或根本不存在路径,
    或直达路径会大于源点到中转点路径加上中转点到终点的路径,
    此时需要将源点到终点的距离更新

*/

//辅助数组初始化函数
void Init(const AMGraph& G, int* Path, int* shortestDis, const int& v, bool* S);
//寻找最短路径<v,vk>的下标k
int findmin(const int* shortestDis, const bool* S, const int& n);
//结束更新,输出最短路径:
void OutshortestPath(const int& v, const int* shortestDis, const int* Path, const AMGraph& G);
//输出该路径上的顶点
void OutvexInpath(const int& v, const int& i, const int* Path, const AMGraph& G);

//计算某源点到其他所有顶点的最短路径
//以邻接矩阵作为存储结构
void ShortestPath_DIJ(const AMGraph& G, const int& v);
//文件名为:DIJ.cpp
#include"DIJ.h"

//inf表示两顶点间没有线路,距离为无穷大
int inf = 99999999;

//辅助数组初始化函数
void Init(const AMGraph& G, int* Path, int* shortestDis, const int& v ,bool*S)
{
    //如果<v,vi>之间有弧,那么vi的直接前驱为v
    for (int i = 0;i < G.vexnum;i++)
    {
        if (inf != G.arcs[v][i])
            Path[i] = v;
        else
            Path[i] = -1;
    }
    //初始化源点到各个顶点之间的最短路径
    for (int i = 0;i < G.vexnum;i++)
    {
        shortestDis[i] = G.arcs[v][i];
    }
    //v自然在最短路径源点集中
    S[v] = true;
}


//计算某源点到其他所有顶点的最短路径
int findmin(const int* shortestDis, const bool* S,const int&n)
{
    int k = 0;
    int minroad = inf;
    for (int i = 0;i < n;i++)
    {
        if (!S[i])
        {
            if (minroad > shortestDis[i])
            {
                k = i;
                minroad = shortestDis[i];
            }
        }
    }
    return k;
}


//输出该路径上的顶点
void OutvexInpath(const int& v, const int& i, const int* Path, const AMGraph& G)
{
    //查找到源点v为止
    if (i == v)
    {
        cout << G.vexs[v];
    }
    else
    {
        //查找下标为i的顶点的直接前驱
        OutvexInpath(v, Path[i], Path, G);
        cout << "->" << G.vexs[i];
    }

}

//结束更新,输出最短路径:
void OutshortestPath(const int& v, const int* shortestDis, const int* Path, const AMGraph& G)
{
    cout << "最短路径输出如下:>" << endl;
    for (int i = 0;i < G.vexnum;i++)
    {
        cout << "从顶点" << G.vexs[v] << "到顶点" << G.vexs[i] << "的最短路径为" << shortestDis[i] << endl;
        //输出该路径上的顶点
        cout << "其路径如下:>" << endl;
        OutvexInpath(v, i, Path, G);
        //输出换行
        cout << endl;
    }
}

//以邻接矩阵作为存储结构
void ShortestPath_DIJ(const AMGraph& G, const int& v)
{
    //判断下标的合法性
    if (v > G.vexnum || v < 0)
    {
        return;
    }
    //首先定义辅助数组
    //1.记录该顶点是否已经加入最短路径终点集合
    bool S[Mvnum] = { 0 };    
    //2.记录源点v0到终点vi的最短路径上,vi的直接前驱顶点在邻接矩阵中的下标
    int Path[Mvnum] = { 0 };
    //3.记录当前源点到各个终点的最短路径
    int shortestDis[Mvnum] = { 0 };
    Init(G, Path, shortestDis, v, S);
    //有向网的顶点个数
    int n = G.vexnum;
    //将其余n-1个顶点加入到S中
    for (int i = 1;i < n;i++)
    {
        //寻找当前源点到没加入S集中的顶点的最短路径(v,vk)
        //并且返回其下标k
        int k = findmin(shortestDis, S, n);
        //将k下标加入到S集
        S[k] = true;
        //通过“中转点”k,更新最短路径
        for (int j = 0;j < n;j++)
        {
            if (!S[j])
            {
                //需要用到<v,k>的最新数据作为判断依据
                if (shortestDis[j] > shortestDis[k] + G.arcs[k][j])
                {
                    //更新最短路径
                    shortestDis[j] = shortestDis[k] + G.arcs[k][j];
                    //更新j的直接前驱为k
                    Path[j] = k;

                }
            }
        }
    }
    //结束更新,输出最短路径:
    OutshortestPath(v, shortestDis, Path, G);
}
//本例可以优化的地方为:定义G,Path,Shortest,S的存储结构为全局变量
//从而减少函数参数

本案例代码可以优化的方面就是,可以直接定义G,Path,Shortest,S等变量为全局变量,从而减少函数的参数个数。

测试代码

//文件名为:test.cpp
#include"DIJ.h"
void test()
{
    AMGraph G;
    CreateUDN(G);
    ShortestPath_DIJ(G, 0);
}

int main()
{
    test();
    return 0;
}

测试结果

迪杰斯特拉,C++,数据结构与算法,数据结构,图论,Powered by 金山文档

算法分析

就空间复杂度而言,由于借助了三个辅助数组,而且数组的大小均为Mvnum(邻接矩阵能够存储的最大顶点个数),故空间复杂度为O(Mvnum)。当然,若n表示顶点个数,如果选择动态开辟内存,则空间复杂度为O(n);

就时间复杂度而言,n依旧表示顶点个数,主循环需要扫描n-1次,而无论是查找最小权值边通过中转点更新最短路径都需要扫描n次,故时间复杂度为O(n^2).若采用邻接表作为存储结构,那么即使更新最短路径的时间会减少(在新加入最短路径集的终点k的邻接表上扫描有限的边,而非扫描n次),当时由于选择最小分量的时间不减,故时间复杂度依旧为O(n^2).

最后说明

即使现实生活中我们只需要源点到特定顶点的最短路径,但是其与求解单源点到达其余全部顶点的最短路径问题一样复杂.文章来源地址https://www.toymoban.com/news/detail-760089.html

到了这里,关于最短路径——迪杰斯特拉算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)

    目录 问题分类  无权图单源最短路径算法 思路 伪代码 时间复杂度 代码实现(C语言) 有权图单源最短路径算法 Dijkstra(迪杰斯特拉)算法 伪代码  时间复杂度  代码实现(C语言) 多源最短路径算法 两种方法 Floyd算法 代码实现(C语言) 最短路径问题的抽象 在网络中,求

    2024年02月08日
    浏览(49)
  • 数据结构第13周 :( 迪杰斯特拉最短路径 + 弗洛伊德求最短路径 + 欧拉回路 + Invitation Cards)

    【问题描述】 在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。 在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。 在本题中,读入一个有向图

    2024年02月13日
    浏览(25)
  • 【数据结构与算法】迪杰斯特拉算法

    介绍 迪杰斯特拉(Dijkstra)算法是 典型最短路径算法 ,用于计算一个节点到其他节点的最短路径。它的主要特点是以中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。 算法过程 设置出发顶点为 v,顶点集合 V{v1,v2,v3…vi},v 到 V 中各顶点的距离构成距离集合

    2024年02月11日
    浏览(35)
  • 数据结构--迪杰斯特拉(Dijkstra)算法

    生活封锁了我们,只要我们的心不死,生活便永远不是一汪死水,而我们,依然会绽放最美的姿态。 戴克斯特拉算法(英语:Dijkstra’s algorithm),又称迪杰斯特拉算法、Dijkstra算法,是由荷兰计算机科学家艾兹赫尔·戴克斯特拉在1956年发现的算法,并于3年后在期刊上发表。

    2024年02月04日
    浏览(37)
  • 大话数据结构-迪杰斯特拉算法(Dijkstra)和弗洛伊德算法(Floyd)

      最短路径,对于图来说,是两顶点之间经过的边数最少的路径;对于网来说,是指两顶点之间经过的边上权值之和最小的路径。路径上第一个顶点为源点,最后一个顶点是终点。   以如下无向图为例:   我们来计算下标为0的顶点,到其他顶点的最短路径,首先定义

    2024年02月06日
    浏览(32)
  • 最短路径:迪杰斯特拉算法

    简介         英文名Dijkstra         作用:找到路中指定起点到指定终点的带权最短路径 核心步骤         1)确定起点,终点         2)从未走过的点中选取从起点到权值最小点作为中心点         3)如果满足 起点到中心点权值 + 中心点到指定其他点的

    2024年02月08日
    浏览(30)
  • 最短路径——迪杰斯特拉算法

    日常生活中常常涉及最短路径问题,如在一个城市交通网中,如何选取从起点到达终点的路径,才能使这一趟旅程的路程最短?或所需时间最少?或所需交通费用最低?诸如此类问题都可以抽象为 求解图的最短路径问题 。我们把 图的顶点 表示为 城市的交通站点 , 边表示交

    2024年02月04日
    浏览(36)
  • 【数据结构】最短路径算法实现(Dijkstra(迪克斯特拉),FloydWarshall(弗洛伊德) )

    最短路径问题 :从在带权有向图G中的某一顶点出发,找出一条通往另一顶点的最短路径,最短也就是沿路径各边的权值总和达到最小。 单源最短路径问题:给定一个图G = ( V , E ) G=(V,E)G=(V,E),求源结点s ∈ V s∈Vs∈V到图 中每个结点v ∈ V v∈Vv∈V的最短路径 针对一个带权

    2024年02月04日
    浏览(40)
  • 迪杰斯特拉算法(求最短路径)

    迪杰斯特拉算法用于查找图中某个顶点到其它所有顶点的最短路径,该算法既适用于无向加权图,也适用于有向加权图。 注意,使用迪杰斯特拉算法查找最短路径时,必须保证图中所有边的权值为非负数,否则查找过程很容易出错。 迪杰斯特拉算法的实现思路 图 1 是一个无

    2024年02月02日
    浏览(29)
  • 迪杰斯特拉算法 – 图的单源最短路径

    迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。迪杰斯特拉算法采

    2024年02月05日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包