Dijkstra算法求最短路径 c++

目录

【问题背景】

【相关知识】

【算法思想】

【算法实现】

【伪代码】

【输入输出】

【代码】


【问题背景】

出门旅游,有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,希望在出发之前知道城市之间的最短路程。

 上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。

我们想知道指定一个城市(源点)到其余各个城市的最短路程,也叫做“单源最短路径”。

【相关知识】

求带权有向图最短路径问题分为两种情况:求从一个顶点到其他各顶点的最短路径,称之为单源最短路径问题;求每对顶点之间的最短路径,称之为多源最短路径问题。

求单源最短路径算法是由狄克斯特拉(Dijkstra)提出的,称为狄克斯特拉算法,是一个按路径长度递增的顺序逐步产生最短路径的方法。

【算法思想】

给定一个图G和一个起始顶点即源点v,求v到其他顶点的最短路径长度及最短路径。

① 初始时,顶点集S只包含源点,即S={v0​},顶点v0​到自已的距离为0。顶点集T包含除v0​外的其他顶点,源点v0​T中顶点i的距离为边上的权(若v0​i有边<v0​,i>)或(若顶点i不是v0​的出边相邻点)。

② 从T中选取一个顶点u,它是源点v0​U中距离最小的一个顶点,然后把顶点u加入S中(该选定的距离就是源点v0​到顶点u的最短路径长度)。

③ 以顶点u为新考虑的中间点,修改源点v0​U中各顶点j(j∈T)的距离。

重复步骤②和③直到S包含所有的顶点即T为空。

【算法实现】

  1. 设置一个数组dist[0..n-1]dist[i]用来保存从源点v0​到顶点i的目前最短路径长度。

  2. path[j]保存源点到顶点j的最短路径,实际上为最短路径上的前一个顶点u,即path[j]=u

  3. 当求出最短路径后由path[j]向前推出源点到顶点j的最短路径。

举例,有如下有向图,求从0到其余顶点的最短路径:

下表给出了上述有向网G中从源点0到其余各顶点的最短路径的求解过程。

最后求出顶点01~6各顶点的最短距离分别为4、5、6、10、916

以求顶点0到顶点4的最短路径为例说明通过path求最短路径的过程:

path[4]=5,path[5]=2,path[2]=1,path[1]=0(源点), 则顶点0到顶点4的最短路径逆为4、5、2、1、0,则正向最短路径为0→1→2→5→4

【伪代码】

  1. 初始化数组dist、path和s;
  2. while (s中的元素个数<n) 2.1 在dist[n]中求最小值,其下标为k; 2.2 输出dist[j]和path[j]; 2.3 修改数组dist和path; 2.4 将顶点vk添加到数组s中;

根据上面的伪代码用邻接矩阵实现Dijkstra算法。

【输入输出】

第一行输入顶点和边的个数vertexNum和arcNum

第二行输入各个顶点的值,为字符型

下面arcNum行依次输入边的起点编号,终点编号和权值

【测试数据】

请输入顶点个数和边的个数:5 6

请输入顶点的值A B C D E

依次输入边的起点编号,终点编号和权值

0 1 1

0 3 1

0 4 1

2 4 1

3 4 1

3 2 1

请输入起始顶点:A

输出从A到各个顶点的最短路径:

从A到B顶点的最短路径长度为:1

从A到B顶点的最短路径为:A->B

从A到C顶点的最短路径长度为:2

从A到C顶点的最短路径为:A->D->C

从A到D顶点的最短路径长度为:1

从A到D顶点的最短路径为:A->D

从A到E顶点的最短路径长度为:1

从A到E顶点的最短路径为:A->E

注意没有路径的情况

请输入顶点个数和边的个数:5 7

请输入顶点的值A B C D E

依次输入边的起点编号,终点编号和权值

0 1 10

0 3 30

0 4 100

1 2 50

2 4 10

3 4 60

3 2 20

请输入起始顶点:B

输出从B到各个顶点的最短路径:

从B到A没有路径.

从B到C顶点的最短路径长度为:50

从B到C顶点的最短路径为:B->C

从B到D没有路径.

从B到E顶点的最短路径长度为:60

从B到E顶点的最短路径为:B->C->E

【代码】

#include<iostream>
#include<algorithm>
#include<string>
#include<limits.h>
using namespace std;
const int MAX_V_NUM = 100;
template <class T>
struct EdgeType
{
    T from, to;     //边依附的两个顶点
    int weight;      //边上的权值
};

template <class T>
class EdgeGraph
{
private:
    char vertex[MAX_V_NUM] = { 0 };  //存放图顶点的数组
    int arc[MAX_V_NUM][MAX_V_NUM] = { INT_MAX };
    int vexNum, arcNum;         //图的顶点数和边数
    int s[MAX_V_NUM] = { 0 };  //存储顶点是否被查找过
    int start;  //输入的起始顶点下标
public:
    EdgeGraph(int vNum, int eNum);
    void Dijkstra();
    int findMinDist(int* dist);  //在dist中查找s[i]为0的最小值元素
    int getIndex(char v)
    {
        for (int i = 0; i < vexNum; i++) {
            if (vertex[i] == v) {
                return i;
            }
        }
        return -1; // 如果找不到顶点,返回-1
    }
    void displayPath(int* dist, int* path);
};
template <class T>
EdgeGraph<T>::EdgeGraph(int vNum, int eNum)
{
    int u, v, w;
    vexNum = vNum;
    arcNum = eNum;

    //arc初始化
    for (int i = 0; i < vexNum; i++)
    {
        for (int j = 0; j < vexNum; j++)
        {
            if (i != j)
                arc[i][j] = INT_MAX;
            else
                arc[i][j] = 0;
        }
    }
    cout << "请输入顶点的值";
    for (int i = 0; i < vexNum; i++)
    {
        cin >> vertex[i];
    }
    cout << "依次输入边的起点编号,终点编号和权值\n";
    for (int i = 0; i < arcNum; i++)
    {
        cin >> u >> v >> w;
        arc[u][v] = w;
    }
    cout << "请输入起始顶点:";
    char c;
    cin >> c;
    start = getIndex(c);
}

//迪杰斯特拉
template <class T>
void EdgeGraph<T>::Dijkstra()
{
    int dist[MAX_V_NUM] = { INT_MAX };
    int path[MAX_V_NUM] = { -1 };
    int num = 0; //记录顶点数
    int min;//记录最小值

    for (int i = 0; i < vexNum; i++)
    {
        dist[i] = arc[start][i];
        if (dist[i] != INT_MAX)
            path[i] = start;
        else
            path[i] = -1;
    }

    //s数组的初始化
    for (int i = 0; i < vexNum; i++)
    {
        s[i] = 0;
    }
    s[start] = 1;//顶点放入集合s。
    num = 1;
    while (num < vexNum) //当顶点数小于图的顶点数
    {
        min = findMinDist(dist); //找到最小值
        for (int i = 0; i < vexNum; i++)
        {    //更新dist和path
            if ((s[i] == 0) && (dist[min] + arc[min][i] < dist[i]) && dist[min] != INT_MAX && arc[min][i] != INT_MAX)  //找到更短的距离,防止距离为无穷的情况
            {             
                dist[i] = dist[min] + arc[min][i];
                path[i] = min; 
            }
        }
        s[min] = 1; //将新生成的点加入s
        num++;
    }
    //打印输出起始点到各顶点的最短路径
    displayPath(dist, path);
}

//找最小值
template <class T>
int EdgeGraph<T>::findMinDist(int* dist)
{
    int min = INT_MAX;
    int index = -1;
    for (int i = 0; i < vexNum; i++) //在dist数组中没有生成树(s[i]=0)的结点中查找
    {
        if (s[i] == 0)
        {
            if (dist[i] < min)
            {
                min = dist[i];
                index = i;
            }
        }
    }
    return index;
}

//输出路径
template <class T>
void EdgeGraph<T>::displayPath(int* dist, int* path)
{
    char result[MAX_V_NUM] = { 0 };
    int k = 0;
    cout << "输出从" << vertex[start] << "到各个顶点的最短路径:\n";
    for (int i = 0; i < vexNum; i++)
    {
        if (i != start)
        {
            if (dist[i] <= 0 || dist[i] == INT_MAX)
            {
                cout << endl;
                cout << "从" << vertex[start] << "到" << vertex[i] << "没有路径." << endl;
            }
            else
            {
                cout << endl;
                cout << "从" << vertex[start] << "到" << vertex[i] << "顶点的最短路径长度为:" << dist[i] << endl;
                cout << "从" << vertex[start] << "到" << vertex[i] << "顶点的最短路径为:" << vertex[start];
                int j = i;
                while (j != start)
                {
                    result[k++] = vertex[j];
                    j = path[j];
                }
                for (int l = k - 1; l >= 0; l--)
                {
                    cout << "->" << result[l];
                }
                k = 0;  //将k归0,进行下一次路径记录
                cout << endl;
            }
        }
    }
    cout << endl;
}

int main()
{
    int vNum, eNum;
    cout << "请输入顶点个数和边的个数:";
    cin >> vNum >> eNum;
    EdgeGraph<char> graph(vNum, eNum);
    graph.Dijkstra();
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/639600.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【iceberg数据一致性】iceberg如何保证高并发数据一致性

在使用iceberg写数据时&#xff0c;一直弄不清楚为什么iceberg写入快&#xff0c;并且能够保证数据的一致性。今天决定搞清楚这个问题&#xff0c;经过查询和理解&#xff0c;写下来。 文件格式 iceberg元数据的文件目前有三个&#xff1a;metadata.json&#xff0c;snap.avro…

MyBatis实用方案,如何使项目兼容多种数据库

系列文章目录 MyBatis缓存原理 Mybatis plugin 的使用及原理 MyBatisSpringboot 启动到SQL执行全流程 数据库操作不再困难&#xff0c;MyBatis动态Sql标签解析 Mybatis的CachingExecutor与二级缓存 使用MybatisPlus还是MyBaits &#xff0c;开发者应该如何选择&#xff1f; 巧…

SVN创建项目分支

目录 背景调整目录结构常规目录结构当前现状目标 调整SVN目录调整目录结构创建项目分支 效果展示 背景 当前自己本地做项目的时候发现对SVN创建项目不规范&#xff0c;没有什么目录结构&#xff0c;趁着创建目录分支的契机&#xff0c;顺便调整下SVN服务器上的目录结构 调整目…

Day36 代码随想录打卡|二叉树篇---翻转二叉树

题目&#xff08;leecode T226&#xff09;&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 方法&#xff1a; 迭代法 翻转二叉树&#xff0c;即从根节点开始&#xff0c;一一交换每个节点的左右孩子节点&#xff0c;然后…

【Arthas】阿里的线上jvm监控诊断工具的基本使用

关于对运行中的项目做java监测的需求下&#xff0c;Arthas则是一个很好的解决方案。 我们可以用来 1.监控cpu 现成、内存、堆栈 2.排查cpu飚高 造成原因 3.接口没反应 是否死锁 4.接口慢优化 5.代码未按预期执行 是分支不对 还是没提交&#xff1f; 6.线上低级错误 能不能不重启…

伦敦金交易商压箱底的交易技法 居然是……

很多伦敦金交易商&#xff0c;也就是我们常说的伦敦金交易平台&#xff0c;或者伦敦金交易服务提供商&#xff0c;他们会和一些资深的市场分析师合作。另外&#xff0c;一般在这些伦敦金交易商内部&#xff0c;也会有一批高手&#xff0c;他们一边在交易&#xff0c;一边在平台…

【设计模式深度剖析】【3】【创建型】【抽象工厂模式】| 要和【工厂方法模式】对比加深理解

&#x1f448;️上一篇:工厂方法模式 | 下一篇:建造者模式&#x1f449;️ 目录 抽象工厂模式前言概览定义英文原话直译什么意思呢&#xff1f;&#xff08;以运动型车族工厂&#xff0c;生产汽车、摩托产品为例&#xff09; 类图4个角色抽象工厂&#xff08;Abstract Fac…

起底震网病毒的来龙去脉

2010年&#xff0c;震网病毒被发现&#xff0c;引起世界哗然&#xff0c;在后续的10年间&#xff0c;陆陆续续有更多关于该病毒的背景和细节曝光。今年&#xff0c;《以色列时报》和《荷兰日报》又披露了关于此事件的更多信息&#xff0c;基于这些信息&#xff0c;我们重新梳理…

使用 Docker 部署 Jenkins 并设置初始管理员密码

使用 Docker 部署 Jenkins 并设置初始管理员密码 每一次开始&#xff0c;我都特别的认真与胆怯&#xff0c;是因为我期待结局&#xff0c;也能够不会那么粗糙&#xff0c;不会让我失望&#xff0c;所以&#xff0c;就多了些思考&#xff0c;多了些拘束&#xff0c;所以&#xf…

软件测试:功能测试-接口测试-自动化测试-性能测试-验收测试

软件测试的主要流程 一、测试主要的四个阶段 1.测试计划设计阶段&#xff1a;产品立项之后&#xff0c;进行需求分析&#xff0c;需求评审&#xff0c;业务需求评级&#xff0c;绘制业务流程图。确定测试负责人&#xff0c;开始制定测试计划&#xff1b; 2.测试准备阶段&…

不小心丢失mfc140u.dll文件怎么办?mfc140u.dll丢失的解决办法

当您发现mfc140u.dll文件不见了或者受损&#xff0c;别担心&#xff0c;我们可以一起解决这个问题&#xff01;首先&#xff0c;您可能会注意到一个小提示&#xff0c;当您尝试打开某些程序时&#xff0c;屏幕上会跳出一个消息说“找不到mfc140u.dll”或者“mfc140u.dll文件缺失…

心识宇宙 x TapData:如何加速落地实时数仓,助力 AI 企业智慧决策

使用 TapData&#xff0c;化繁为简&#xff0c;摆脱手动搭建、维护数据管道的诸多烦扰&#xff0c;轻量代替 OGG、DSG 等同步工具&#xff0c;「CDC 流处理 数据集成」组合拳&#xff0c;加速仓内数据流转&#xff0c;帮助企业将真正具有业务价值的数据作用到实处&#xff0c…

Python的selenium爬取

1.selenium 1.1.前言 使用python的requests模块还是存在很大的局限性&#xff0c;例如&#xff1a;只发一次请求&#xff1b;针对ajax动态加载的网页则无法获取数据等等问题。特此&#xff0c;本章节将通过selenium模拟浏览器来完成更高级的爬虫抓取任务。 1.2.什么是seleniu…

学习单向链表带哨兵demo

一、定义 在计算机科学中&#xff0c;链表是数据元素的线性集合&#xff0c;其每个元素都指向下一个元素&#xff0c;元素存储上并不连续。 1.可以分三类为 单向链表&#xff0c;每个元素只知道其下一个元素是谁 双向链表&#xff0c;每个元素知道其上一个元素和下一个元素 …

抖音小店不能做无货源了吗?当然不是,而是玩法更先进了!

大家好&#xff0c;我是电商糖果 自从2023年抖音小店开始严查无货源&#xff0c;不少商家被平台处罚&#xff0c;被逼无奈退出抖音小店。 网上关于抖音小店不能做无货源的声音越来越多。 可是一年多过去&#xff0c;大家渐渐的发现&#xff0c;平台内还是有很多无货源商家&a…

Sping源码(八)—registerBeanPostProcessors

序言 之前我们用大量的篇幅介绍过invokeBeanFactoryPostProcessors()方法的执行流程。 而invokeBeanFactoryPostProcessors的主要逻辑就是遍历执行实现了BeanDefinitionRegistryPostProcesso类(主要是针对BeanDefinition的操作)和BeanFactoryPostProcessor(主要针对BeanFacrot…

spring-boot集成slf4j(二)logback配置详解

一、configuration 根节点&#xff1a;configuration&#xff0c;作为顶级标签&#xff0c; 可以用来配置一些lockback的全局属性&#xff0c;常见的属性如下&#xff1a; &#xff08;1&#xff09;scan“true” &#xff1a;scan是否开启自动扫描&#xff0c;监控配置文件更…

XShell-连接-Centos 7

XShell 连接Centos 7 一.准备 安装XShell XShell下载地址&#xff1a; 在虚拟机上安装Centos 7&#xff0c;具体操作自行学习 二.Centos 7的准备 1.网络适配器修改为NAT 2.获取IP 输入命令&#xff1a; ip addr我的Centos 7对外IP为192.168.174.129 三.XShell连接Cento…

Big Demo Day第十三期活动即将启幕,Web3创新项目精彩纷呈,PEPE大奖等你抽取

5月28号在香港数码港 Big Demo Day第十三期 活动即将拉开帷幕&#xff0c;活动将汇集众多Web3领域的创新项目&#xff0c;为参会者带来一场科技与智慧交融的盛宴。在这里&#xff0c;你不仅能深入了解区块链、AI等前沿技术的最新应用&#xff0c;还能有机会赢取丰厚的PEPE大奖。…

使用maven-helper插件解决jar包冲突

发现问题 maven-helper分析问题 如上所述&#xff0c;问题就是依赖版本冲突了&#xff0c;出现版本冲突的原因是因为由于Maven具有依赖传递性&#xff0c;所以当你引入一个依赖类的同时&#xff0c;其身后的依赖类也一起如过江之鲫纷至沓来了。 举个例子&#xff1a;   A依赖…