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

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


前言

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

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


一、Dijkstra算法思想

求最短路径实际上就是求两个顶点之间权值和最小的那条路径。在前面的找路问题中,我们的权值就是距离,所以距离和最短的就是最短路径,这是一个很形象的例子。当然,这个权值也可是其它如费用、时间等的单位。记住这只是一个抽象的概念。
我们用一张图来表示这个例子:
C++算法:单源最短路径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文章来源地址https://www.toymoban.com/news/detail-488207.html

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

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

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

相关文章

  • Dijkstra算法——单源最短路径(指定一个节点(源点)到其余各个顶点的最短路径)

    国庆期间,小明打算从1号城市出发,在五天假期中分别去往不同的城市(2,3,4,5,6)旅游,为减轻负担,他想要知道1号城市到各个城市之间的最短距离。 现在需要设计一种算法求得源点到任意一个城市之间的最短路径。该问题的求解也被称为“单源最短路径”。 在所有

    2024年02月03日
    浏览(41)
  • 【数据结构】最小生成树(Prim算法,普里姆算法,普利姆)、最短路径(Dijkstra算法,迪杰斯特拉算法,单源最短路径)

    问题解答 (1)最小生成树(Minimal Spanning Tree)的定义 生成树的代价 :设 G ( V , E ) G(V,E) G ( V , E ) 是一个无向连通网图,生成树上 各边的权值之和 称为 生成树的代价 。 最小生成树 :在图 G G G 所有生成树中, 代价最小的生成树 为 最小生成树 。 (2)最小生成树(MST)的性

    2024年02月11日
    浏览(31)
  • 单源最短路径(spfa,Dijkstra, bellman-ford)

    目录  Dijkstra 原理:基于贪心。 为什么 Dijkstra 不能处理有负边的情况 Bellman-ford 原理:动态规划, 实质见floyd的另一篇博客 1,能找负环, 2,有变数限制的最短路径 spfa 原理 spfa怎么求负环, 原理:基于贪心。 第一步 初始化距离,dist[1] = 0, 一号点到起点的距离为0, 其他点

    2024年02月04日
    浏览(38)
  • 算法提高-图论-单源最短路的扩展应用

    多源点单终点最短路建图: 创建虚拟源点(创建虚拟源点的时候以是spfa为例 可以在建图的时候建出来,也可以在spfa这直接入队,也是虚拟源点的意思) 反向建图变成单源点多终点,然后遍历终点的dist即可找出最短路 这题挺简单的就不详细说了,主要是第一次遇到计数问题

    2024年02月16日
    浏览(35)
  • 算法提高-图论-单源最短路的综合应用

    多次dijkstra求每个点到其它点的最短距离, 此时相当于建好了一张图,每个点之间的最短距离都知道了,接下来dfs搜一下怎么走最短即可 一篇博客解释了为什么一个正向建图求最小值,反向建图求最大值 根本思想是保证1到n的买卖是连通的

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

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

    2024年02月05日
    浏览(29)
  • 算法提高-图论-单源最短路的建图方式

    建图 找出一个牧场,它到其他牧场的距离之和最小 我是这么理解的,djsktra是一个贪心的思想,加法里面不能加负数我就不说了 求乘法最大值的时候为什么边权必须0-1,因为在乘法最大值里面有一个边权大于1的话那不就等价于求加法最小值的时候有一个边权为负数的么,d

    2024年02月08日
    浏览(30)
  • 【算法入门&搜索法】走迷宫|单源最短路径1

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:算法百炼成神 本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面

    2024年02月03日
    浏览(53)
  • 贝尔曼福特算法——负权值单源最短路径

    title: 贝尔曼福特算法——负权值单源最短路径 date: 2023-05-16 11:42:26 tags: 数据结构与算法 **问题:**具有负权值非环图的单源最短路径算法 git地址:https://github.com/944613709/HIT-Data-Structures-and-Algorithms 对图中的边进行V-1轮遍历,对所有的边松弛(对每条边v1-v2,如果d[v2]+Weight(v1-v2)

    2024年02月14日
    浏览(29)
  • 广度优先搜索(BFS)算法求解单源最短路径问题

      单源最短路径问题是图论中一类重要且常见的应用问题。在这个问题中,我们需要找到从一个给定源节点到其他节点的最短路径。广度优先搜索(BFS)算法是一种常用且有效的求解这一问题的算法。   本篇博客将重点讨论单源最短路径问题及其实际应用,同时简要介绍

    2024年02月13日
    浏览(33)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包