文章目录

  • 引言:单条路径的标配算法
  • 进阶:记录多条路径的改进版Dijkstra算法:
    • 1. 对`P[]`数组进行扩充
    • 2. 求最短路径条数
    • 3. 输出所有最短路径

引言:单条路径的标配算法

一个普通、标配、差强人意的Dijkstra算法包含以下过程(任意数据结构教材均有售):


// Dijkstra:
//// 图采用存储结构:邻接矩阵
int G[MAX_NODES][MAX_NODES]; // G[i][j],表示i到j有路径,其值为路径长度。没有路径则设为INF
int N; //结点个数int D[MAX_NODES]; //表示从源点v0开始,到各个结点的路径长度
int P[MAX_NODES]; //用来描述路径。P[j] = i,即表示:经过i才能到达j。
bool finale[MAX_NODES]; //finale[i] 为 true 则表示通往i结点的最短路径已经确定,加入了最短路径集合。// 算法主体
void DIJ(int N, int v0, int D[], int P[], bool finale[]) {int v, w; //循环指针for (v=0; v<N; ++v) { // 初始化过程D[v] = G[v0][v]; P[v] = v0; finale[v] = false;}finale[v0] = true; D[v0] = 0; P[v0] = -1; //对源点无需求最短路径for (int i=1; i<N; ++i) { //主循环,共循环N-1次int min = INF;for (w=0; w<N; ++w)if (finale[w] == false && D[w] < min){v = w; min = D[w];}finale[v] = true; //当前最近结点加入集合。for (w=0; w<N; ++w) {if (finale[w] == false && min + G[v][w] < D[w]) {// 更新最短路径D[w] = min + G[v][w];P[w] = v;}}}
}
// end

输出路径的算法:

// 这里要用到上面的P数组, 起点v0,终点v
// 因为P数组是倒着存放路径的,所以需要反向输出,即利用栈的特性。
void print_path(int v0, int v, int P[]) {STACK st;int c = v; st.push(v);while(c != v0) {c = P[c];st.push(c);}while(st不空) {printf("%d", st.top());st.pop();}
}

然而,很多时候我们面对的不仅仅只有“最短路径”。客户有时需要我们给出多条代价相近且最小的方案。比如下面这张图:

这里每一条start -> end的路径都是最短路径。下面就介绍如何应对这一情形。

进阶:记录多条路径的改进版Dijkstra算法:

我们基于之前所给出的算法,其中有一个P[]数组,其用来记录路径Path。
P[j] = i,当且仅当:弧(i, j)在j的最短路径上,即最短路径上j的前一个结点是i

1. 对P[]数组进行扩充

现在,将P扩充为一个二维数组。P[j]存储的不再是一个数,而是一个新的数组,在这个数组里存储的每一个数,都是j的某一条最短路径的直接前驱i
P数组现在的定义如下:

#define MAX_NODES ...
#define MAX_PATHS ...
int P[MAX_NODES][MAX_PATHS];
// int P[MAX_NODES]; //以前的P数组,只能存储一条最短路径

或者是动态数组:(强烈推荐!!)

#include <vector>
vector<int> P[MAX_NODES];

这时,每当通往j有一条新的最短路径时,我们都在P[j]这个一位数组里再添上一个新的成员。

这里强烈推荐动态数组的原因是:

  1. P[]数组里每个一维数组的长度,都是不一样的,如果是普通数组,那么对P[j]添加成员时,需要先求出当前数组的长度,或者将数组的下标0位置空出,已存储当前的数组长度。
  2. 每个结点所在最短支路的条数,相差甚大:有的处于枢纽结点,有10+条最短路径,有的只是过渡结点,只有一条最短路径。如果采用静态数组统一分配空间,会造成普遍的浪费现象。
  3. 如果是动态数组,就不必考虑这些问题,可以直接使用push_back或者是append方法。

此时,算法中初始化数组、以及更新最短路径的那部分代码,应该修改如下:
P[v] = v0,修改为:P[v].clear(); P[v].push_back(v0);
P[v0] = -1,修改为:P[v].clear();
P[w] = v,修改为:P[w].clear(); P[w].push_back(v);
同时还要增加下列语句(最短路径相等):
if (finale[w] == false && min + G[v][w] == D[w]) {
D[w] = min + G[v][w]; P[w].push_back(v); }
除了最短路径相等的情况,其余每次更新路径都必须清空一维数组clear(),原因在于每次更新最短路径后,之前的所有路径,就成为错误的路径了。
最后的算法如下:


// Dijkstra: 加强版
//// 图采用存储结构:邻接矩阵
int G[MAX_NODES][MAX_NODES]; // G[i][j],表示i到j有路径,其值为路径长度。没有路径则设为INF
int N; //结点个数int D[MAX_NODES]; //表示从源点v0开始,到各个结点的路径长度
vector<int> P[MAX_NODES]; //用来描述路径。P[j] 包含了 i,即表示://至少存在一条最短路径,使得经过i才能到达j。
bool finale[MAX_NODES]; //finale[i] 为 true 则表示通往i结点的最短路径已经确定,加入了最短路径集合。// 算法主体
void DIJ(int N, int v0, int D[], vector<int> P[], bool finale[]) {int v, w; //循环指针for (v=0; v<N; ++v) { // 初始化过程D[v] = G[v0][v]; P[v].clear(); P[v].push_back(v0); finale[v] = false;}finale[v0] = true; D[v0] = 0; P[v0].clear(); //对源点无需求最短路径for (int i=1; i<N; ++i) { //主循环,共循环N-1次int min = INF;for (w=0; w<N; ++w)if (finale[w] == false && D[w] < min){v = w; min = D[w];}finale[v] = true; //当前最近结点加入集合。for (w=0; w<N; ++w) {if (finale[w] == false && min + G[v][w] < D[w]) {// 更新最短路径D[w] = min + G[v][w];P[w].clear(); P[w].push_back(v);} elseif (finale[w] == false && min + G[v][w] == D[w]) {//增添新的最短路径D[w] = min + G[v][w];P[w].push_back(v);}}}
}
// end

2. 求最短路径条数

求最短路径的条数,就是从终点end往回返,数组P[end]的长度,就是最终通往end的最短路径条数,然后再依次遍历end的所有最短路径的直接前驱,以递归的方式,求各个中间结点的最短路径的支路的条数,直到回到起点v0

// 求以v0为起点,o为终点,的最短路径的条数:get_paths_num
int get_paths_num(int v0, int o, vector<int> P[]) {if (o == v0) return 1;   // 递归终止的条件: 起点==终点 int count = 0;for (int i = 0; i < P[o].size(); ++i) //遍历终点的直接前驱,支路条数累加起来count += get_paths_num(v0, P[o][i], P);return count;
}

3. 输出所有最短路径

单条最短路径算法,利用栈结构来输出路径,这是因为P[]数组是从终点开始,存储最短路径上每一个结点的直接前驱。也可以不使用栈的结构,而是利用reverse将字符串或者容器进行倒序。
对于加强后的多条最短路径算法,P[]数组这个特性并没有发生变化,所以利用递归的思想,其输出算法大同小异。
我们建立一个字符串数组,每当一轮递归结束,都把生成的路径字符串在屏幕上打印、换行,并推入数组:

vector<int> P[MAX_NODES];
vector<string> V;
// 函数功能:打印并保存 v0->o 的所有最短路径
// cur字符串作递归辅助用,首次调用务必使cur为空串
void print_path(int v0, int o, string cur = "") {cur += o + '0';if (o == v0) { //退出递归的条件:起点==终点,输出路径,保存至V数组reverse(cur.begin(), cur.end());cout << cur << endl;V.push_back(cur);return;}for (int i = 0; i < P[o].size(); ++i)print_path(v0, P[o][i], cur);
}

单源最短路径Dijkstra算法升级:出现多条最短路径,输出之?相关推荐

  1. 分支限界法:单源最短路径--dijkstra算法

    单源最短路径–dijkstra算法 前面已经多次介绍过dijkstra算法是贪心算法,是动态规划,实际上可以从分支限界的角度来理解: 分支限界法 分支限界法,实际上就是回溯法,一般意义的回溯法是基于深 ...

  2. 最短路径——Dijkstra算法以及二叉堆优化(含证明)

    一般最短路径算法习惯性的分为两种:单源最短路径算法和全顶点之间最短路径.前者是计算出从一个点出发,到达所有其余可到达顶点的距离.后者是计算出图中所有点之间的路径距离. 单源最短路径 Dijkstra算 ...

  3. python棋盘最短路径_Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例...

    本文实例讲述了Python数据结构与算法之图的最短路径(Dijkstra算法).分享给大家供大家参考,具体如下: # coding:utf-8 # Dijkstra算法--通过边实现松弛 # 指定一个 ...

  4. 遍历所有点的最短路径python_图遍历算法之最短路径Dijkstra算法

    一.最短路径问题(shortest path problem) 最短路径问题是图论研究中一个经典算法问题,旨在寻找图中两节点或单个节点到其他节点之间的最短路径.根据问题的不同,算法的具体形式包括: 确 ...

  5. 最短路径-Dijkstra算法与Floyd算法

    最短路径-Dijkstra算法与Floyd算法 原文:https://www.cnblogs.com/smile233/p/8303673.html 一.最短路径 ①在非网图中,最短路径是指两顶点之间 ...

  6. C++实现单源最短路算法

    1.单源最短路算法 n个处理器,第一个处理器要广播消息到其他所有的处理器,求需要时间最短是多少(从第一个点出发,求到其他点最短路的最大值) 2.思路 这个基本上没啥可说.看代码理解就是. 3.代码实现 ...

  7. 使用邻接矩阵实现有向图最短路径Dijkstra算法

    题目描述: 用邻接矩阵存储有向图,实现最短路径Dijkstra算法,图中边的权值为整型,顶点个数少于10个. 输入描述 首先输入图中顶点个数和边的条数: 再输入顶点的信息(字符型): 再输入各边及其权 ...

  8. 最短路径——Dijkstra算法与Floyd算法

    最短路径 Dijkstra算法 C语言代码实现 代码解析 Floyd算法 算法解析 C语言代码实现 最短路径问题 最短路径问题是我们经常会面临的一种决策问题.在图论中,非网图(边没有权值)的最短路径就 ...

  9. 最短路径 Dijkstra算法的Matlab代码实现

    为了搞清楚最短路径的算法过程,自己编写代码实现dijkstra算法寻找路径 % 文件名:dijkstra.m % 时间:2020年9月12日 % 来源:https://blog.csdn.net/li ...

最新文章

  1. Flutter开发之ListView添加HeaderView和FooterView-2(39)
  2. Windows核心编程 第2 5章 未处理异常和C ++异常(上)
  3. bdd行为驱动开发 java_行为驱动开发(BDD)如何与领域驱动设计(DDD)结合?
  4. 基于SpringBoot和Vue的个人博客系统
  5. 2010年 Web的五项技术和五个趋势
  6. 控制台应用程序换换为窗体应用_Epic为开发者设计了一套iPhone使用的运动捕捉应用程序...
  7. linux下sendmail邮件系统安装操作记录
  8. 避开使用XAML的性能陷阱
  9. [2019.3.25]多项式求逆
  10. 对比Excel学Python(二)数据可视化
  11. [java] 简单的ConcurrentHashMap
  12. git注意事项(持续更新中)
  13. Unity模拟行星轨道(一)
  14. 8uftp怎么连到,8uftp怎么连到服务器
  15. 利用shell脚本,实现腾讯云DNSPod进行DDNS动态域名解析ipv6地址
  16. 展讯平台关机闹钟自动开机不响铃问题及提前开机时间设置
  17. vant表单checkbox验证问题
  18. The Coder Vol.6:小程序有大事
  19. codeforces 1256f
  20. 关于STM32+OLED显示屏的简单应用

热门文章

  1. 标签云 tagCloud 简单实现原理(还算好看)
  2. Linux修改资源限制详解
  3. Java设计模式----------外观模式
  4. 基于verilog的除法器的实现
  5. IDesign C#编程规范(之四)
  6. 通过powershell+winscp在远程服务器上跑本地的代码
  7. spring-boot-starter大力出奇迹
  8. Cocos2d-x怪物智能AI怪物也有智商--之游戏开发《赵云要格斗》(6) cocos2dx 3.3移植版
  9. linux入门常用知识点part1(干货系列小郭学习Linux的第一天)
  10. 审美的变迁 回顾Android 系统进化史