C++算法:单源最短路径Dijkstra

news2024/12/27 19:25:12

文章目录

  • 前言
  • 一、Dijkstra算法思想
  • 二、算法实现
    • 1、建立图
    • 2、代码实现
  • 总结


前言

如果你有一份北京地图,想从中关村走到三元桥,那么怎样能找出实现这一目的的最短路径呢?一种可能的方法就是将这两点之间所有的路线都找出来,然后求出每条路线的距离,找出最短的路线。但是仔细想想我们就会发现这种办法几乎是不可行的,因为这样的路线太多了,而且有些路线是完全不值得考虑的,比如你从中关村到昌平再到三元桥。那这样的问题有没有更科学的解决办法呢?答案是肯定的。

我们把从中关村到三元桥之间所有路线抽象成一个加权有向图,所有路段的交点就是
图的顶点,那么这个问题就可以抽象成在加权有向图G中,求出起始点中关村,到终点
三元桥之间的最短路径。这就是我们将要介绍的最短路径问题。最短路径问题有多种变体,本文只讨论有向无环图上的单源最短路径问题。


一、Dijkstra算法思想

求最短路径实际上就是求两个顶点之间权值和最小的那条路径。在前面的找路问题中,我们的权值就是距离,所以距离和最短的就是最短路径,这是一个很形象的例子。当然,这个权值也可是其它如费用、时间等的单位。记住这只是一个抽象的概念。
我们用一张图来表示这个例子:
在这里插入图片描述
在上图所示的带权有向无环图中,共有10个顶点,我们可以将其理解成路口,有19条带权边,我们可以理解成各段路及距离。现在假设起点是节点0,终点是节点9。我们要找出这两点之间的最短路径应该怎么办?

根据贪心算法的思想,假设我们从起点开始,取每个与起点最近的顶点来看,那结果是0–4–7–8–6–9。距离和是21显然这样得到的路径不是最优的,这个路径还不如0–3–2–9(距离和19)事实上简单的贪心算法思想往往能得到一个不算最差的解。

dijkstra算法也是贪心算法思想,但不能简单的直接每个顶点取最小权值的边。它的思想是:起点到终点的权值和最小,那么起点到最短路径的终点间的任一顶点的权值和也是最小的。这就有点加入动态规划的子问题最优解的思想了。那么我们只要找出这条最短路径上的每个顶点就可以了。

核心思想就是从起点开始不断往外查找,直到查找完所有顶点。初始时,我们只知道起点,以及与起点直接相连的边,我们不断的根据这个边的权值访问找到的最小权值和的边,并在程序运行时不断更新这个边的末端顶点到起点的距离,保证它一定是已知的最短距离。这样当我们访问完所有顶点,自然就得到了最短路径。

二、算法实现

1、建立图

无论如何,我们首先要用代码来表示上图,很明显地二维数组是一个表示各边权值的好方法。
代码如下(示例):

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

class Graph{
    private:
        int vertex;      //顶点数
        int** matrix;    //有向图关系矩阵
        bool* visited;    //存储是否已访问
        int* dist;        //存储到起点的权值和
        int* pre;         //存储上一个节点

    public:
        const int maxium = 10000;                //最大值,表示不存在的边
        Graph(const int edges, const int nodes, int arr[][3]){
            vertex = nodes;
            visited = new bool[vertex];    
            dist = new int[vertex];       
            pre = new int[vertex];
            matrix = new int* [vertex];          //生成有向图关系矩阵
            for (int i=0; i<vertex; ++i){
                pre[i] = -1;
                dist[i] = maxium;
                visited[i] = false;
                matrix[i] = new int[9];
                for (int j=0; j<vertex; j++){
                    matrix[i][j] = maxium;
                }
            }
            for (int i=0; i<edges; ++i){          //生成有向图关系,maxium为不连接
                matrix[arr[i][0]][arr[i][1]] = arr[i][2];
            }
        }

        ~Graph(){
            delete[] visited;
            delete[] matrix;
            delete[] pre;
            delete[] dist;
        }

看上去和以前关于图论的文章矩阵表示法的代码很像,嗯~ 事实上我就是复制的。所以充满了个人风格。这里我们定义了 maxium=10000 来表示无穷大的权值,也就是没路。int vertex; 表示顶点数。 int** matrix; 有来存储有向图的各边及权值,同样是以下标表示边。 bool* visited; 用来存储顶点是否已访问。 int* dist; 用来存储顶点到起点的权值和。int* pre; 存储从起点到本顶点的最短路径中的上一个顶点,最终可以根据这个数组得到任两个顶点间的最短路径。

2、代码实现

代码如下(示例):

        void dijkstra(int s, int end){
            dist[s] = 0;
            visited[s] = true;
            for (int i=0; i<vertex; i++){   //与起点相连的节点到起点的距离
                if (matrix[s][i] < maxium){
                    dist[i] = matrix[s][i];
                    pre[i] = s;
                }
            }
            int curr = s;
            for (int i=0; i<vertex; ++i){  //开始寻找
                int tmp = maxium;
                for (int j=0; j<vertex; j++){   //找出离起点最小权值的点,命名为curr
                    if (!visited[j] && dist[j]<tmp){
                        tmp = dist[j];
                        curr = j;
                    }
                }
                visited[curr] = true;  //开始访问前面找到的离起点最近点curr
                for (int k=0; k<vertex; ++k){
                    int new_dist = maxium;   //更新权值
                    if (!visited[k] && matrix[curr][k] < maxium){
                        new_dist = dist[curr] + matrix[curr][k];
                        if (new_dist < dist[k]){
                            dist[k] = new_dist;
                            pre[k] = curr;    //记录当前顶点的前驱
                        }
                    }
                }
            }
            show(s, end);
        }

        void show(int start, int end){  //显示路径
            stack<int> out;
            int n = end;
            while (n != start){   //将各顶点前驱压入栈
                out.push(n);
                n = pre[n];
            }
            out.push(start);
            vector<int> path;   //倒入向量,使顺序正常
            while (out.size()){
                path.push_back(out.top());
                out.pop();
            }
            for (int i=0; i<path.size(); ++i){
                cout << path[i] << " ";
            }
            cout << endl;
        }
};

代码中Dijkstra函数就是实现算法的函数。函数有两个参数s和end,分别表示起始节点和结束节点。函数将所有节点从起始节点的距离初始化为无穷大,除了起始节点,它被初始化为0。然后它找到距离起始节点最近的节点并更新其邻居的距离。这个过程重复进行,直到所有节点都被访问过。matrix 变量表示图的邻接矩阵,其中每个元素表示两个节点之间的边的权重。dist 数组存储每个节点到起始节点的距离,而 pre存储最短路径中每个节点的前一个节点。

show 函数用于打印从起始节点到结束节点的最短路径。


总结

本文实现了任两点间的有向无环图的最短路径计算。Dijkstra算法是一种用于在图中查找两个节点之间的最短路径的算法。该算法使用贪心策略,每次找到距离起始节点最近的节点并更新其邻居的距离。这个过程重复进行,直到所有节点都被访问过。

该算法的时间复杂度为O(V^2),其中V是节点数。如果使用堆优化,时间复杂度可以降低到O(ElogV),其中E是边数。

在实现中,我们使用邻接矩阵来表示图,其中每个元素表示两个节点之间的边的权重。以下是测试代码:

int main(){
    int arr[][3] = {{0,1,8},{0,3,16,},{0,4,7},{1,3,9},{1,5,5},{2,9,2},
                   {3,2,1},{3,6,10},{3,8,12},{4,7,5},{4,3,9},{4,8,7},{5,3,2},
                   {5,2,11},{6,2,13},{6,9,2},{7,6,8},{8,7,1},{8,6,6}};

    Graph t(19, 10, arr);
    t.dijkstra(0, 9);
    return 0;
}

最终结果是:0 1 5 3 2 9

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openSUSE项目近日宣布openSUSE Leap 15.5的发布和全面供应

openSUSE项目近日宣布openSUSE Leap 15.5的发布和全面供应&#xff0c;该版本是openSUSE变体的最新稳定版本&#xff0c;针对那些希望为其个人电脑提供基于SUSE Linux Enterprise 15的经过良好测试的操作系统的用户。 openSUSE Leap 15.5是在openSUSE Leap 15.4的一年后推出的&…

Vue中如何进行音频可视化与音频频谱展示

Vue中如何进行音频可视化与音频频谱展示 随着音频应用程序的不断发展&#xff0c;音频可视化和音频频谱展示成为了重要的功能。在Vue应用程序中实现音频可视化和音频频谱展示可以帮助用户更好地了解音频文件的内容和特征。本文将介绍如何在Vue应用程序中实现音频可视化和音频频…

Opensearch基本介绍

OpenSearch 是一个社区驱动的开源搜索和分析套件&#xff0c;开发人员使用该套件来摄取、搜索、可视化和分析数据。 OpenSearch 由数据存储和搜索引擎 (OpenSearch)、可视化和用户界面 (OpenSearch Dashboards) 以及服务器端数据收集器 (Data Prepper) 组成。 用户可以使用一系…

把数字中国,建立在行业感知的底座上

5月23日&#xff0c;国家互联网信息办公室发布了《数字中国发展报告&#xff08;2022年&#xff09;》。报告显示&#xff0c;2022年中国数字经济规模达到50.2万亿元&#xff0c;占国内生产总值比重提升至41.5%&#xff0c;总量居世界第二。如今数字中国最主要的发展挑战&#…

MIFARE - 1

2一般说明 飞利浦根据ISO/IEC 14443A开发了用于非接触式智能卡的MIFAREMF1 IC S50。通信层&#xff08;MIFARERF接口&#xff09;符合ISO/IEC 14443A标准的第2部分和第3部分。安全层采用经过现场验证的CRYPTO1流密码&#xff0c;用于MIFAREClassic系列的安全数据交换。 MIFARE…

GPT中的temperature参数不是用在对话的而是用在调用OPEN API过程中的

前言 自从吴恩达OPENAI《ChatGPT 提示工程》放出后,各个层面反响热列。很多人看到了temperature这个参数,都以为在对话中或者说对话的末尾放上一个temperature=0-2的值就可以达到让GPT极大的发挥出自我创造能力、甚至写文章天马行空。 笔者这边觉得有义务指出这种用法是完全…

OpenAI ChatGPT 使用示例(程序员)

1.编程应用 1.1. 生成例子代码(Coding Generation) ChatGPT帮助我们生产我们需要的例子代码。而且准确率很高。即使你不懂某一种语言也没关系&#xff0c;一定程度上较低了程序员的的门槛。 我有三组数据&#xff0c;第一组是星期一到星期五&#xff0c;第二组是这一天的具体…

第七十八天学习记录:高等数学:微分方程(宋浩板书)

微分方程&#xff08;Differential equation&#xff09;是描述自然现象中变量之间关系的数学语言。它是以函数、导数、微分等数学概念为基础的方程&#xff0c;揭示了自然现象中变量之间的内在联系。微分方程在物理学、工程学、生物学、经济学、统计学等各领域都有广泛的应用。…

C++线程库(2)

C线程库&#xff08;2&#xff09; 线程同步互斥锁条件变量与互斥锁的搭配使用举例1举例2举例3 线程同步 在C线程库&#xff08;1&#xff09;的博客中说了互斥量只能解决多个线程访问共享资源的问题&#xff0c;但是很明显没有次序感&#xff0c;而线程安全就是不同线程访问资…

最短路径算法-迪杰斯特拉(Dijkstra)算法(记录最短路径和距离)

原理&#xff1a; Dijkstra算法是解决**单源最短路径**问题的**贪心算法** 它先求出长度最短的一条路径&#xff0c;再参照该最短路径求出长度次短的一条路径 直到求出从源点到其他各个顶点的最短路径。 首先假定源点为u&#xff0c;顶点集合V被划分为两部分&#xff1a;集合…

chatgpt赋能python:Python字符串去除多余空格

Python字符串去除多余空格 随着Python在各个领域的应用越来越广泛&#xff0c;很多工程师都会遇到字符串去除多余空格的需求。而Python提供了简单的方法来解决这个问题&#xff0c;本文将详细介绍这些方法。 介绍 在Python中&#xff0c;字符串是很常见的数据类型&#xff0…

Linux环境下的工具(yum,gdb,vim)

一&#xff0c;yum yum其实是linux环境下的一种应用商店&#xff0c;主要用centos等版本。它也有三板斧&#xff1a;yum list,yum remove,yum install。当然不是说他只有这三个命令&#xff0c;还有yum search等等。在这直说以上三个。 yum list其实是查看你所能安装的软件包…

puppet 入门详解 超详细!!!

目录 一、puppet概述 二、Puppet的工作模式是什么&#xff1f; 三、Puppet的适用场景是什么&#xff1f; 四、原理 &#xff08;一&#xff09;工作模型 &#xff08;二&#xff09;工作流程 &#xff08;三&#xff09;使用模型 1、单机使用模型 2、master/agent 模型 &…

Vue中如何进行自动化部署与持续集成(CI/CD)

Vue中如何进行自动化部署与持续集成&#xff08;CI/CD&#xff09; 随着云计算和容器技术的广泛应用&#xff0c;自动化部署和持续集成&#xff08;CI/CD&#xff09;已经成为现代软件开发过程中必不可少的环节。Vue作为一款流行的前端框架&#xff0c;也可以使用自动化部署和…

解决:闹钟设置的自定义歌曲响铃时不会播放仅震动【Apple Music】【iOS】

文章目录 1、问题描述2、解决策略3、Q&A4、感受5、Tips 1、问题描述 自带铃声和震动脑瓜子嗡嗡的&#xff0c;幸好有apple music&#xff0c;在闹钟中可以轻松地选择你放入资料库中的任意一首音乐作为铃声。 奇怪的是&#xff0c;闹钟响起&#xff0c;仅震动&#xff0c;没…

chatgpt赋能python:Python怎么过滤非数字

Python怎么过滤非数字 在实际编程过程中&#xff0c;我们常常遇到要对一些数据进行处理&#xff0c;其中经常需要过滤掉非数字的数据&#xff0c;以保证程序能够正常运行。在Python中&#xff0c;若要过滤非数字&#xff0c;可以采用如下几种方法。 方法一&#xff1a;使用正…

chatgpt赋能python:Python中如何输入以0开头的数字?

Python中如何输入以0开头的数字&#xff1f; 在Python编程中&#xff0c;可能会遇到需要输入以0开头的数字的情况。然而&#xff0c;当我们尝试在Python shell或代码中输入以0开头的数字时&#xff0c;我们会发现Python会自动将其转换为八进制格式。 为什么Python会将以0开头…

使用MDK-ARM(KEIL V5)创建一个工程(有图有文字)

使用keil v5创建工程是一个比较复杂的过程&#xff0c;还希望读者能够耐下心来&#xff0c;过于浮躁会使创建过程出错&#xff0c;导致编译器无法编译等等许多问题。 言归正传&#xff0c;我们接下来开始说明创建过程&#xff0c;说明过程以图片为主&#xff0c;文字为辅&…

谷粒商城第四天-前端基础

目录 一、前言 二、学习的内容 一、ES6新语法 1.1 var与let 1.2 const 1.3 解构表达式的使用 1.4 字符串Api的使用 1.5 函数优化 1.6 箭头函数 1.7 对象优化 1.8 map和reduce 1.9 promise异步编排 1.10 模块化&#xff08;export和import的使用&#xff09;…

chatgpt赋能python:Python如何输出Pi

Python如何输出Pi Python是一门强大且易于学习的编程语言。它可以完成各种任务&#xff0c;包括数学计算和科学计算。在这篇文章中&#xff0c;我们将介绍如何使用Python输出圆周率Pi。 介绍 圆周率是一个重要的数学常数&#xff0c;用π表示。它代表了一个圆的周长与其直径…