一、问题介绍

1.求K条最短路径的必要性

最短路径问题分为:

单源最短路径

所有顶点对间的最短路径

共同的缺陷:

这里的最短路径指两点间最短的那一条路径,不包括次短、再次短等路径。这样的最短路径问题比较狭义。

在实际情况中,例如:用户在使用咨询系统或决策支持系统时,希望得到最优的决策参考外,还希望得到次优、再次优等决策参考,这同样反映在最短路径问题上。因此有必要将最短路径问题予以扩展和延伸,称为K条最短路径问题,即不但要求得最短路径,还要求得次短、再次短等路径。

2.KSP问题定义

KSP问题是对最短路径问题的推广,它除了要确定最短路径之外,还要确定次短路径、第三短路径,...,知道找到第K短路径。用Pi表示从起点s到终点t的第i短路径,KSP问题是确定路径集合Pk={p1,p2,p3,...,pk},使得满足以下3个条件:

1)K条路径是按次序产生的,即对于所有的i(i=1,2,...,K-1),pi是在pi+1之前确定;

2)K条路径是按长度从小到大排列的,即对于所有的i(i=1,2,...,K-1),都有c(pi)

3)这K条路径是最短的,即对于所有的p∈Pst-PK,都有c(pk)

3.相关概念

用于说明偏离概念的图

设起点为 1 ,终点为 5 。p1、p2、p3 分别表示起点到终点间最短路径、第二短路径、第三短路径。

1)偏离点

p3相对于p1的偏离节点为节点 2

2)偏离边

p3相对于p1的偏离边为(2,4)

3)偏离路径

p3相对于p1的(2,4,5)

4.KSP算法分类

1) 一般KSP问题

对路径没有任何限制

2) 限定无环KSP问题

要求所有路径都是简单路径,不能有环

当路径中所有节点都不同时,称为无环路径

两类KSP问题具有不同的特征,分别提出了不同的求解算法,分别称之为

一般的KSP算法

限定无环KSP算法

K最短路径算法分类

二、思路

下面列出用Yen's算法求KSP的代码。该算法是Yen在1971年提出的以其名字命名的Yen算法。算法采用了递推法中的偏离路径算法思想,适用于非负权边的有向无环图。

1.流程图

Q:将什么样的点做为偏离点

在pk的基础上求pk+1时,将pk的路径上除终点d之外的节点分别作为偏离点

Q:求Vi到终点d的最短路径

设起点为s,终点为t,偏离点为Vi。求偏离点到终点的最短路径时要注意两个问题

防止从起点到终点的整体路径有环

从Vi到t的最短路径不能包含s到Vi的路径上的任何节点

避免与已经在结果列表中的路径重复

从Vi发出的边不能与结果列表中的路径p1,p2,...pk,上从Vi发出边的相同

这个体现在代码上就是:在用Dijkstra算法算最短路径时对数组的初始化要进行特别处理

// 不能走的边

if (unavailableEdges != null && unavailableEdges.size() != 0) {

for (MyPath p: unavailableEdges) {

int index = p.path.indexOf(startIndex);

if (index >= 0 && (index + 1) >= 0) {

dist[index + 1] = Double.MAX_VALUE;

path[index + 1] = -1;

}

}

}

// 不能走的点

if (unavailableNodeIndexs != null && unavailableNodeIndexs.size() != 0) {

for (Integer point: unavailableNodeIndexs) {

set[point] = 1;

}

}

Q:每次找到新的最短路径时,需将该路径从候选链列表中移除,并加入到结果列表中

Q:候选列表中的路径不能重复

所以选择用Set来存储候选路径,去重

Q:如何从候选列表中选出合适的路径

如果只有一条路径权值和最小的路:将该路径返回;

如果路径权值和最小的路有多条:从其中选出节点数最少的路径。

2.手工模拟求K最短路径流程

求C到H的10条最短路径

节点加载内存后存储在Node[] nodes数组中,各个节点在数组中的存储位置如下,下面用各个点的数组下标进行说明。表格中括号中备注为路径权重。

1)通过最短路径算法Dijkstra得到C到H的最短路径

p1=C-E-F-H,即0-2-3-5

2)在p1的基础上求p2

遍历完各个偏离点后的情况:

从集合B中选出路径0->2->4->5(7)移除,并加入到集合A中,作为p2

3)在p2的基础上求p3

遍历完各个偏离点后的情况:

从集合B中选出路径0->1->3->5(8)移除,并加入到集合A中,作为p3

4)在p3的基础上求p4

遍历完各个偏离点后的情况:

从集合B中选出路径0->1->3->5(8)移除,并加入到集合A中,作为p4

5)在p4的基础上求p5

遍历完各个偏离点后的情况:

从集合B中选出路径0->2->3->4->5(8)移除,并加入到集合A中,作为p5`

6)在p5的基础上求p6

遍历完各个偏离点后,没有新的候选路径加入集合B中

从集合B中选出路径0->1->3->4->5(11)移除,并加入到集合A中,作为p6

7)在p6的基础上求p7

遍历各个偏离点时求最短路径均不存在,故遍历完各个偏离点后,没有新的候选路径加入集合B中,从集合B中选出路径0->2->1->3->4->5(11)移除,并加入到集合A中,作为p7

8)在p7的基础上求p8

遍历各个偏离点时求最短路径均不存在,故遍历完各个偏离点后,没有新的候选路径加入集合B中,候选集合为空,不能再继续向下寻找。故从C到H共有7条路径。

三、代码

1.输入上述图

6 9

11 C

12 D

13 E

14 F

15 G

16 H

11 12 3

11 13 2

12 14 4

13 12 1

13 14 2

13 15 3

14 15 2

14 16 1

15 16 2

11 16 10

2.代码

package road;

import util.NavigationUtil;

import java.util.*;

/**

*

* author : 杨丽金

* time : 2018/11/14

* desc : 有关于图的最短路径

* version: 1.0

*

*/

public class ShortestPath {

public static class MyPath {

// 路径上的各个节点对应的数组下标(从起点到终点)

public List < Integer > path;

// 路径总权值

public double weight;

// 路径上节点个数:通过path.size()得到

public MyPath() {}

public MyPath(List < Integer > path, double weight) {

this.path = path;

this.weight = weight;

}

@Override

public String toString() {

return "MyPath{" +

"path=" + path +

", weight=" + weight +

'}';

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

MyPath path1 = (MyPath) o;

return path != null ? path.equals(path1.path) : path1.path == null;

}

@Override

public int hashCode() {

int result;

long temp;

result = path != null ? path.hashCode() : 0;

temp = Double.doubleToLongBits(weight);

result = 31 * result + (int)(temp ^ (temp >>> 32));

return result;

}

}

/**

* 用Yen's KSP算法从图中找出从startIndex到endIndex的K条最短路径

*

* @param g

* @param startIndex:起始节点的数组下标

* @param endIndex:终止节点的数组下标

* @param K:要求的最短路径数目

* @return

*/

public List < MyPath > KSP_Yen(MyGraph g, int startIndex, int endIndex, int K) {

// 结果列表

List < MyPath > result = new ArrayList < > ();

// 候选路径列表

Set < MyPath > candidatePaths = new HashSet < > ();

// 候选路径列表中权值最小的路径,及其对应的节点个数

// 第一条最短路径

MyPath p1 = getSingleShortestPath_dijkstra(g, startIndex, endIndex, null, null);

result.add(p1);

int k = 1;

List < Integer > pk = p1.path;

while (k < K) {

/*

求第k+1条最短路径

*/

// 遍历每一个偏离点

for (int i = 0; i <= pk.size() - 2; i++) {

// 1,pk路径中起点到偏离点Vi的路径权值

double w1 = 0;

for (int j = 0; j <= i - 1; j++) {

w1 += NavigationUtil.getEdgeWight(g, pk.get(j), pk.get(j + 1));

}

// 2,偏离点到终点的最短路径

MyPath viToDestinationSP = getSingleShortestPath_dijkstra(g,

pk.get(i), endIndex, pk.subList(0, i), result);

if (viToDestinationSP != null) {

// 说明从这个偏离点可以到达终点

MyPath temp = new MyPath();

List < Integer > tempPath = new ArrayList < > (pk.subList(0, i));

tempPath.addAll(viToDestinationSP.path);

temp.path = tempPath;

temp.weight = w1 + viToDestinationSP.weight;

// 加入候选列表

if (!candidatePaths.contains(temp)) {

candidatePaths.add(temp);

}

}

}

if (candidatePaths == null || candidatePaths.size() == 0) {

// 没有候选路径,则无需再继续向下求解

break;

} else {

// 从候选路径中选出最合适的一条,移除并加入到结果列表

MyPath fitPath = getFitPathFromCandidate(candidatePaths);

candidatePaths.remove(fitPath);

result.add(fitPath);

k++;

pk = fitPath.path;

}

}

return result;

}

/**

* 从候选列表中得到一条路径作为pk+1

* 要求:1)该路径的权值和最小;2)路径经过节点数最少

* @param candidatePaths:候选列表

* @return

*/

private MyPath getFitPathFromCandidate(Set < MyPath > candidatePaths) {

MyPath result = new MyPath(null, Double.MAX_VALUE);

for (MyPath p: candidatePaths) {

// 对于每一条路径

if (p.weight < result.weight) {

result = p;

}

if (p.weight == result.weight && p.path.size() < result.path.size()) {

result = p;

}

}

return result;

}

/**

* 用Dijkstra算法得到从startIndex到endIndex的一条最短路径

*

* @param g

* @param startIndex 起始节点的数组下标

* @param endIndex 终止节点的数组下标

* @param unavailableNodeIndexs:求最短路径时不可用的节点(数组下标)

* @param unavailableEdges:求最短路径时不可用的边

* @return

*/

public MyPath getSingleShortestPath_dijkstra(MyGraph g, int startIndex, int endIndex,

List < Integer > unavailableNodeIndexs, List < MyPath > unavailableEdges) {

if (startIndex == -1) {

// throw new Exception("getSingleShortestPath_dijkstra()起始点编号输入错误");

}

if (endIndex == -1) {

// throw new Exception("getSingleShortestPath_dijkstra()终止点编号输入错误");

}

int[] set = new int[g.n]; // 是否已并入集合,该点是否已找到最短路径

// s到i的最短路径长度

double[] dist = new double[g.n];

// s到i的最短路径上i的前一个节点编号

int[] path = new int[g.n];

// 初始化数组

set[startIndex] = 1;

for (int i = 0; i < g.n; i++) {

if (i == startIndex) { // 源点

dist[i] = 0;

path[i] = -1;

} else {

if (NavigationUtil.isConnected(g, startIndex, i)) {

dist[i] = NavigationUtil.getEdgeWight(g, startIndex, i);

path[i] = startIndex;

} else {

dist[i] = Double.MAX_VALUE;

path[i] = -1;

}

}

}

// 不能走的边

if (unavailableEdges != null && unavailableEdges.size() != 0) {

for (MyPath p: unavailableEdges) {

int index = p.path.indexOf(startIndex);

if (index >= 0 && (index + 1) >= 0) {

dist[p.path.get(index + 1)] = Double.MAX_VALUE;

path[p.path.get(index + 1)] = -1;

}

}

}

// 不能走的点

if (unavailableNodeIndexs != null && unavailableNodeIndexs.size() != 0) {

for (Integer point: unavailableNodeIndexs) {

set[point] = 1;

}

}

// 需进行n-2轮循环

for (int i = 0; i < g.n - 2; i++) {

int k = -1;

double min = Double.MAX_VALUE;

// 找出dist[]中最小的(太贪心了)

for (int j = 0; j < g.n; j++) {

if (set[j] == 1) {

continue;

}

if (dist[j] < min) {

min = dist[j];

k = j;

}

}

if (k == -1) {

// 说明从源点出发与其余节点不连通,无法再向下进行扩展

break;

}

set[k] = 1; // 把节点k并入

// 修改dist[]、path[]

for (int j = 0; j < g.n; j++) {

if (set[j] == 1) {

continue;

}

if (NavigationUtil.isConnected(g, k, j)) {

double temp = dist[k] + NavigationUtil.getEdgeWight(g, k, j);

if (temp < dist[j]) {

dist[j] = temp;

path[j] = k;

}

}

}

}

System.out.println("运行Dijkstra算法后的数组情况为:");

System.out.print("set[]:");

for (int i = 0; i < g.n; i++) {

System.out.print(set[i] + "\t");

}

System.out.println();

System.out.print("dist[]:");

for (int i = 0; i < g.n; i++) {

System.out.print(dist[i] + "\t");

}

System.out.println();

System.out.print("path[]:");

for (int i = 0; i < g.n; i++) {

System.out.print(path[i] + "\t");

}

System.out.println();

// 输出

if (dist[endIndex] == Double.MAX_VALUE) {

// 说明没有最短路径,两点不连通

System.out.println("两点之间不连通");

return null;

} else {

System.out.println("节点" + g.nodes[startIndex].nodeId + "到节点" +

g.nodes[endIndex].nodeId + "的最短路径长度为:" + dist[endIndex] + ",具体路径是:");

MyPath result = new MyPath();

result.path = getMinimumPath(g, startIndex, endIndex, path);

result.weight = dist[endIndex];

return result;

}

}

/**

* 输出从节点S到节点T的最短路径

*

* @param sIndex:起始节点在数组中下标

* @param tIndex:终止节点在数组中下标

*/

private List < Integer > getMinimumPath(MyGraph g, int sIndex, int tIndex, int[] path) {

List < Integer > result = new ArrayList < > ();

Stack < Integer > stack = new Stack < > ();

stack.push(tIndex);

int i = path[tIndex];

while (i != -1) {

stack.push(i);

i = path[i];

}

while (!stack.isEmpty()) {

System.out.print(g.nodes[stack.peek()].nodeId + ":" + g.nodes[stack.peek()].nodeName + "-->");

result.add(NavigationUtil.getIndex(g, g.nodes[stack.pop()].nodeId));

}

System.out.println();

return result;

}

}

3.输出

sps: [MyPath {

path = [0, 2, 3, 5], weight = 5.0

}, MyPath {

path = [0, 2, 4, 5], weight = 7.0

}, MyPath {

path = [0, 1, 3, 5], weight = 8.0

}, MyPath {

path = [0, 2, 1, 3, 5], weight = 8.0

}, MyPath {

path = [0, 2, 3, 4, 5], weight = 8.0

}, MyPath {

path = [0, 1, 3, 4, 5], weight = 11.0

}, MyPath {

path = [0, 2, 1, 3, 4, 5], weight = 11.0

}]

参考文献

[1]K条最短路径问题综述

[2]韩海玲. 基于城市路网的最短路径算法研究与应用[D]. 中北大学, 2017.

[3]徐涛, 丁晓璐, 李建伏. K最短路径算法综述[J]. 计算机工程与设计, 2013, 34(11):3900-3906.

[4]K条最短路径算法(KSP, k-shortest pathes):Yen's Algorithm

k则最短路径matlab,Yen的K条最短路径算法(KSP)相关推荐

  1. 最短路径分析之两点之间的k条最短路径

    目录 前言 Yen算法 数据准备 求取k条最短路径(KSP) 预览结果 前言 在用高德地图导航的时候都会发现,高德会推荐多条线路.我们之前做的dijkstra算法只能实现两点之间的1条最短路径的计算. ...

  2. 数据处理 | MATLAB实现KNN(K近邻)缺失数据填补

    数据处理 | MATLAB实现KNN(K近邻)缺失数据填补 目录 数据处理 | MATLAB实现KNN(K近邻)缺失数据填补 基本介绍 插补描述 程序设计 参考资料 基本介绍 用于缺失值插补的单变量方 ...

  3. 聚类分析 | MATLAB实现k-Means(k均值聚类)分析

    目录 聚类分析 | MATLAB实现k-Means(k均值聚类)分析 k-均值聚类简介 相关描述 程序设计 学习小结 参考资料 致谢 聚类分析 | MATLAB实现k-Means(k均值聚类)分析 k ...

  4. matlab单位阶跃序列,产生单位阶跃序列的MATLAB程序如下k.ppt

    产生单位阶跃序列的MATLAB程序如下k 一.实验目的 掌握典型连续信号和离散序列的MATLAB描述方法 掌握序列的常用运算方法 二.实验设备 * * 实验二 基本信号的产生及其运算 信息工程学院 网 ...

  5. matlab计算表达式 k,刘卫国 Matlab 例题 1-3章

    第一章 例1.1 分别绘制函数和的曲线. x=-2*pi:pi/180:2*pi; plot(x,2.^(-abs(x)),':',x,sin(x)); 例1.2 求方程 2x5-3x3 +71x2- ...

  6. Matlab 主成分分析与K均值聚类分析实验报告

    Matlab 主成分分析与K均值聚类分析实验报告 提示:数据资源在本CSDN号的上传资料中直接领取 1 引言 数据:gyzb.mat(按顺序对应每一列)为:31个省市区的国有控股企业的主要指标(包括: ...

  7. B - Frogger(最短路之多条最短路径中最大权值的最小值)

    题目描述 Freddy Frog is sitting on a stone in the middle of a lake. Suddenly he notices Fiona Frog who i ...

  8. 【Matlab路径规划】蚁群算法机器人大规模栅格地图最短路径规划【含源码 1860期】

    一.代码运行视频(哔哩哔哩) [Matlab路径规划]蚁群算法机器人大规模栅格地图最短路径规划[含源码 1860期] 二.蚁群算法及栅格地图简介 随着机器人技术在诸多领域的应用, 如机器人协作焊接.灾 ...

  9. 5分钟k线数据 存储_成功率极高的“分时K线战法”:15分钟K线战法+30分钟K线战法...

    实战看盘最常用的就是日.周.月等K线图.这些图形对于我们分析个股走势的确帮助很大,然而遇到调整时,就显示了其宏观有余.微观不足的缺点.但假如我们用分时K线图进行微观的观察与分析,就可以找到一些个股在调 ...

最新文章

  1. 该不该放弃嵌入式,单片机这条路?
  2. 2位图灵奖得主、7位院士等一众AI大佬云集共议数字化转型 | CNCC 2021
  3. JavaScript 30 - 3 学习笔记
  4. git fetch比较差异
  5. 非等高cell实战(01)-- 实现微博页面
  6. OAuth 2.0 的探险之旅
  7. Facebook账户 “ 解封、防封、养号 ” 知识要点
  8. spring小实验 用spring的方式管理JDBC
  9. iframe 父级元素查找
  10. python开发服务程序_Python 编写Windows服务程序:将Python作为Windows服务启动 | 学步园...
  11. python3入门经典100例-ZH奶酪:编程语言入门经典100例【Python版】
  12. Jquery的parent和parents(找到某一特定的祖先元素)
  13. java继承案例分析,Java day5【第十四章】继承案例分析
  14. 修复VSS 2005
  15. java 进度条实现原理_java进度条功能的实现原理是什么?实例展示
  16. 不同光栅的结构特点、优缺点以及光栅的选择
  17. 揭秘|Axway API在银行业的应用
  18. 阿里云服务器能怎么玩呢?
  19. Esp32+Dht11+MQTT+Mysql实现测温数据传至数据库
  20. 一文读懂间隙锁(Gap Locks)

热门文章

  1. linux系统网络基础知识
  2. 美的集团计算机综合面试群面,美的集团面试经验
  3. S2B2C微分销系统 快速打造多种运营模式
  4. WTL 界面设计篇(CSkinRichEdit)
  5. 曝支付宝漏洞可随意更改他人密码,回应称在核查
  6. 2021年安全员-C证模拟试题及安全员-C证实操考试视频
  7. ios助手开发系列(三):打开设备连接,获取设备基本信息
  8. python canvas教程_Canvas
  9. 【Case】result set exceeds the maximum size(100M)
  10. bigemap如何通过3D-MAX制作三维地图模型