我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++的最短路径怎么计算

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++的最短路径怎么计算

这篇文章主要介绍“C++的最短路径怎么计算”,在日常操作中,相信很多人在C++的最短路径怎么计算问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++的最短路径怎么计算”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

最短路径

最短路径恐怕是图的各种算法中最能吸引初学者眼球的了——在地图上找一条最短的路或许每个人都曾经尝试过。下面我们用计算机来完成我们曾经的“愿望”。

在图的算法中有个有趣的现象,就是问题的规模越大,算法就越简单。图是个复杂的结构,对于一个特定问题,求解特定顶点的结果都会受到其他顶点的影响——就好比一堆互相碰撞的球体,要求解特定球体的状态,就必须考虑其他球体的状态。既然每个顶点都要扫描,如果对所有的顶点都求解,那么算法就非常的简单——无非是遍历吗。然而,当我们降低问题规模,那很自然的,我们希望算法的规模也降低——如果不降低,不是杀鸡用牛刀?但是,正是由于图的复杂性,使得这种降低不容易达到,因此,为了降低算法的规模,使得算法就复杂了。

在下面的介绍中,清楚了印证了上面的结论。人的认知过程是从简单到复杂,虽然表面看上去,求每对顶点之间的最短路径要比特定顶点到其他顶点之间的最短路径复杂,但是,就像上面说的,本质上,前者更为简单。下面的介绍没有考虑历史因素(就是指哪个算法先提出来),也没有考虑算法提出者的真实想法(究竟是谁参考了或是没参考谁),只是从算法本身之间的联系来做一个阐述,如有疏漏,敬请原谅。

准备工作

一路走下来,图类已经很“臃肿”了,为了更清晰的说明问题,需要“重起炉灶另开张”,同时也是为了使算法和储存方法分开,以便于复用。

首先要为基本图类添加几个接口。

template <class name, class dist, class mem>  class Network  {  public:  int find(const name& v) { int n; if (!data.find(v, n)) return -1; return n; }  dist& getE(int m, int n) { return data.getE(m, n); }  const dist& NoEdge() { return data.NoEdge; }  };  template <class name, class dist>  class AdjMatrix  {  public:  dist& getE(int m, int n) { return edge[m][n]; }  };  template <class name, class dist>  class Link  {  public:  dist& getE(int m, int n)  {  for (list::iterator iter = vertices[m].e->begin();  iter != vertices[m].e->end() && iter->vID < n; iter++);  if (iter == vertices[m].e->end()) return NoEdge;  if (iter->vID == n) return iter->cost;  return NoEdge;  }  };

然后就是为了最短路径算法“量身定做”的“算法类”。求某个图的最短路径时,将图绑定到算法上,例如这样:

Network<char, int, Link<char, int> > a(100);  //插入点、边……  Weight<char, int, Link<char, int> > b(&a);   #include "Network.h"  template <class name, class dist, class mem>  class Weight  {  public:  Weight(Network* G) : G(G), all(false), N(G->vNum())  {  length = new dist*[N]; path = new int*[N];  shortest = new bool[N]; int i, j;  for (i = 0; i < N; i++)  {  length[i] = new dist[N]; path[i] = new int[N];  }  for (i = 0; i < N; i++)  {  shortest[i] = false;  for (j = 0; j < N; j++)  {  length[i][j] = G->getE(i, j);  if (length[i][j] != G->NoEdge()) path[i][j] = i;  else path[i][j] = -1;  }  }  }  ~Weight()  {  for (int i = 0; i < N; i++) { delete []length[i]; delete []path[i]; }  delete []length; delete []path; delete []shortest;  }  private:  void print(int i, int j)  {  if (path[i][j] == -1) cout << "No Path" << endl; return;  cout << "Shortest Path: "; out(i, j); cout << G->getV(j) << endl;  cout << "Path Length: " << length[i][j] << endl;  }  void out(int i, int j)  {  if (path[i][j] != i) out(i, path[i][j]);  cout << G->getV(path[i][j]) << "->";  }  dist** length; int** path; bool* shortest; bool all; int N;  Network* G;  };

发现有了构造函数真好,算法的结果数组的初始化和算法本身分开了,这样一来,算法的基本步骤就很容易看清楚了。

所有顶点之间的最短路径(Floyed算法)

从v1到v2的路径要么是v1->v2,要么中间经过了若干顶点。显然我们要求的是这些路径中最短的一条。这样一来,问题就很好解决了——最初都是源点到目的点,然后依次添加顶点,使得路径逐渐缩短,顶点都添加完了,算法就结束了。

void AllShortestPath()//Folyed  {  if (all) return;  for (int k = 0; k < N; k++)  {  shortest[k] = true;  for (int i = 0; i < N; i++)  for (int j = 0; j < N; j++)  if (length[i][k] + length[k][j] < length[i][j])  {  length[i][j] = length[i][k] + length[k][j];  path[i][j] = path[k][j];  }  }  all = true;  }

单源最短路径(Dijkstra算法)

仿照上面的Floyed算法,很容易的,我们能得出下面的算法:

void ShortestPath(int v1)  {  //Bellman-Ford  for (int k = 2; k < N; k++)  for (int i = 0; i < N; i++)  for (int j = 0; j < N; j++)  if (length[v1][j] + length[j][i] < length[v1][i])  {  length[v1][i] = length[v1][j] + length[j][i];  path[v1][i] = j;  }  }

这就是Bellman-Ford算法,可以看到,采用Floyed算法的思想,不能使算法的时间复杂度从O(n3)降到预期的O(n2),只是空间复杂度从O(n2)降到了O(n),但这也是应该的,因为只需要原来结果数组中的一行。因此,我并不觉得这个算法是解决“边上权值为任意值的单源最短路径问题”而专门提出来的,是Dijkstra算法的“推广”版本,他只是Floyed算法的退化版本。

显然,Floyed算法是经过N次N2条边迭代而产生最短路径的;如果我们想把时间复杂度从O(n3) 降到预期的O(n2),就必须把N次迭代的N2条边变为N条边,也就是说每次参与迭代的只有一条边——问题是如何找到这条边。

先看看边的权值非负的情况。假设从顶点0出发,到各个顶点的距离是a1,a2……,那么,这其中的最短距离an必定是从0到n号顶点的最短距离。这是因为,如果an不是从0到n号顶点的最短距离,那么必然是中间经过了某个顶点;但现在边的权值非负,一个比现在这条边还长的边再加上另一条非负的边,是不可能比这条边短的。从这个原理出发,就能得出Dijkstra算法,注意,这个和Prim算法极其相似,不知道谁参考了谁;但这也是难免的事情,因为他们的原理是一样的。

void ShortestPath(const name& vex1, const name& vex2)//Dijkstra  {  int v1 = G->find(vex1); int v2 = G->find(vex2);  if (shortest[v1]) { print(v1, v2); return; }  bool* S = new bool[N]; int i, j, k;  for (i = 0; i < N; i++) S[i] = false; S[v1] = true;  for (i = 0; i < N - 1; i++)//Dijkstra Start, like Prim?  {  for (j = 0, k = v1; j < N; j++)  if (!S[j] && length[v1][j] < length[v1][k]) k = j;  S[k] = true;  for (j = 0; j < N; j++)  if (!S[j] && length[v1][k] + length[k][j] < length[v1][j])  {  length[v1][j] = length[v1][k] + length[k][j];  path[v1][j] = k;  }  }  shortest[v1] = true; print(v1, v2);  }

如果边的权值有负值,那么上面的原则不再适用,连带的,Dijkstra算法也就不再适用了。这时候,没办法,只有接受O(n3) Bellman-Ford算法了,虽然他可以降低为O(n*e)。不过,何必让边的权值为负值呢?还是那句话,合理的并不好用。

特定两个顶点之间的最短路径(A*算法)

其实这才是我们最关心的问题,我们只是想知道从甲地到乙地怎么走最近,并不想知道别的——甲地到丙地怎么走关我什么事?自然的,我们希望这个算法的时间复杂度为O(n),但是,正像从Floyed算法到Dijkstra算法的变化一样,并不是很容易达到这个目标的。

让我们先来看看Dijkstra算法求特定两个顶点之间的最短路径的时间复杂度究竟是多少。显然,在上面的void ShortestPath(const name& vex1, const name& vex2)中,当S[v2]==true时,算法就可以中止了。假设两个顶点之间直接的路径是他们之间的路径最短的(不需要经过其他中间顶点),并且这个路径长度是源点到所有目的点的最短路径中最短的,那么***次迭代的时候,就可以得到结果了。也就是说是O(n)。然而当两个顶点的最短路径需要经过其他顶点,或者路径长度不是源点到未求出最短路径的目的点的最短路径中最短的,那就要再进行若干次迭代,对应的,时间复杂度就变为O(2n)、O(3n)……到了***才求出来(迭代了N-1次)的就是O(n2)。

很明显的,迭代次数是有下限的,最短路径上要经过多少个顶点,至少就要迭代多少次,我们只能使得最终的迭代次数接近最少需要的次数。如果再要减低算法的时间复杂度,我们只能想办法把搜索范围的O(n)变为O(1),这样,即使迭代了N-1次才得到结果,那时间复杂度仍为O(n)。但这个想法实现起来却是困难重重。

仍然看Dijkstra算法,***步要寻找S中的顶点到S外面顶点中最短的一条路径,这个min运算使用基于比较的方法的时间复杂度下限是O(longN)(使用败者树),然后需要扫描结果数组的每个分量进行修改,这里的时间复杂度就只能是O(n)了。原始的Dijkstra算法达不到预期的目的。

现在让我们给图添加一个附加条件——两点之间直线最短,就是说如果v1和v2之间有直通的路径(不经过其他顶点),那么这条路径就是他们之间的最短路径。这样一来,如果求的是v1能够直接到达的顶点的最短路径,时间复杂度就是O(1)了,显然比原来***的O(n)(这里实际上是O(logN))提高了效率。

这个变化的产生,是因为我们添加了“两点之间直线最短”这样的附加条件,实际上就是引入一个判断准则,把原来盲目的O(n)搜索降到了O(1)。这个思想就是A*算法的思想。关于A*算法更深入的介绍,恕本人资料有限,不能满足大家了。有兴趣的可以到网上查查,这方面的中文资料实在太少了,大家做好看E文的准备吧。

不同于现有的教科书,我把求最短路径的算法的介绍顺序改成了上面的样子。我认为这个顺序才真正反映了问题的本质——当减低问题规模时,为了降低算法的时间复杂度,应该想办法缩小搜索范围。而缩小搜索范围,都用到了一个思想——尽可能的向接近***结果的方向搜索,这就是贪婪算法的应用。

如果反向看一遍算法的演化,我们还能得出新的结论。Dijkstra算法实际上不用做n2次搜索、比较和修改,当求出最短路径的顶点后,搜索范围已经被缩小了,只是限于储存结构,这种范围的缩小并没有体现出来。如果我们使得这种范围缩小直接体现出来,那么,用n次Dijkstra算法代替Floyed算法就能带来效率上的提升。A*算法也是如此,如果用求n点的A*算法来代替Dijkstra算法,也能带来效率的提升。

但是,每一步的进化实际上都伴随着附加条件的引入。从Floyed到Dijkstra是边上的权值非负,如果这个条件不成立,那么就只能退化成Bellman-Ford算法。从Dijkstra到A*是另外的判断准则的引入(本文中是两点之间直线最短),如果这个条件不成立,同样的,只能采用不完整的Dijkstra(求到目的顶点结束算法)。

到此,关于“C++的最短路径怎么计算”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++的最短路径怎么计算

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++的最短路径怎么计算

这篇文章主要介绍“C++的最短路径怎么计算”,在日常操作中,相信很多人在C++的最短路径怎么计算问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++的最短路径怎么计算”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-17

C#图表算法之最短路径怎么实现

本篇内容主要讲解“C#图表算法之最短路径怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#图表算法之最短路径怎么实现”吧!从一个顶点到达另一个顶点的成本最小的路径。我们采用一个一般性的模
2023-06-30

matlab最短路径算法怎么应用

在MATLAB中,可以使用Graph and Digraph对象来实现最短路径算法。首先,你需要创建一个Graph对象,然后通过添加边来定义图的结构。然后,你可以使用内置的最短路径函数来计算两个节点之间的最短路径。下面是一个使用MATLAB
2023-10-07

python最短路径算法怎么选择

小编给大家分享一下python最短路径算法怎么选择,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!说明1、解决任意两个节点之间的最短距离,用Floyd。2、解决单源最短路径问题,有负边时用Bellman-Ford,无负边时用
2023-06-20

C++最短路径Dijkstra算法如何实现

这篇文章主要介绍“C++最短路径Dijkstra算法如何实现”,在日常操作中,相信很多人在C++最短路径Dijkstra算法如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++最短路径Dijkstra
2023-07-05

Floyd和dij算法计算最短路径有什么区别

xFloyd与dij算法在计算最短路径方面都有各自的优势和应用场景。区别主要包括:1.算法基础不同;2.时间复杂度不同;3.空间复杂度不同;4.应用范围不同;5.实现难度不同;6.结果表示不同;7.应用领域不同。xFloyd算法基于动态规划思想,用于求解图中所有顶点对之间的最短路径;dij算法是基于贪心思想,主要用于求解从某一源点到图中所有其他顶点的最短路径。
Floyd和dij算法计算最短路径有什么区别
2023-10-29

python中怎么利用Dijkstra算法求最短路径

python中怎么利用Dijkstra算法求最短路径,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。  从某源点到其余各顶点的最短路径  Dijkstra算法可用
2023-06-02

C++最短路径Dijkstra算法的分析与具体实现详解

经典的求解最短路径算法有这么几种:广度优先算法、Dijkstra算法、Floyd算法。本文是对 Dijkstra算法的总结,该算法适用于带权有向图,可求出起始顶点到其他任意顶点的最小代价以及对应路径,希望对大家有所帮助
2023-03-10

java实现最短路径算法之Dijkstra算法的示例

这篇文章主要介绍了java实现最短路径算法之Dijkstra算法的示例,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、知识准备:1、表示图的数据结构用于存储图的数据结构有多
2023-05-31

怎么使用c语言动态规划求解最短路径

在C语言中使用动态规划求解最短路径,可以按照以下步骤进行:1. 定义一个二维数组来表示图中各个节点之间的距离。假设有n个节点,则可以定义一个n×n的二维数组dist[][],其中dist[i][j]表示节点i到节点j的距离。2. 初始化di
2023-08-18

C语言怎么寻找无向图两点间的最短路径

这篇文章主要讲解了“C语言怎么寻找无向图两点间的最短路径”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言怎么寻找无向图两点间的最短路径”吧!1.简介无向图是图结构的一种。本次程序利用邻接
2023-06-08

Python和Matlab怎么实现蚂蚁群算法求解最短路径

这篇文章主要介绍“Python和Matlab怎么实现蚂蚁群算法求解最短路径”,在日常操作中,相信很多人在Python和Matlab怎么实现蚂蚁群算法求解最短路径问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”P
2023-06-29

C++怎么求二叉树的最大路径和

本篇内容主要讲解“C++怎么求二叉树的最大路径和”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++怎么求二叉树的最大路径和”吧!求二叉树的最大路径和Given a non-empty bina
2023-06-20

C++怎么查找字串的连接最长路径

这篇文章主要讲解了“C++怎么查找字串的连接最长路径”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++怎么查找字串的连接最长路径”吧!题目描述给定n个字符串,请对n个字符串按照字典序排列。
2023-06-19

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录