一、Dijkstra算法
1.定义概览:单源最短路径算法
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。
时间复杂度是O(n2)
注意:该算法要求图中不存在负权边。
2.算法思想:
Dijkstra算法采用的是一种贪心的策略,声明一个数组distance来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T。
初始化,原点 s 的路径权重被赋为 0 (distance[s] = 0,如果是有向图也可以设置为无穷大,因为有向图需要区别是否有到达自己的路径)。若对于顶点 s 存在能直接到达的边(s,m),则把distance[m]设为weight(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。同时,把顶点s加入集合T。
然后,从distance数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点。
接着,我们需要看看新加入的顶点是否可以到达其他顶点,并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在distance中的值。
重复上述动作,直到T中包含了图的所有顶点。
3、Dijkstra算法示例演示
下面我求下图,从任一源点(因为原博主的图是选择v1作为源点,咋们就用它吧,个人比较懒,懒得去自己画图了,只要自己能讲清楚算法实现,部分拿来主义也未尝不可,本人实现的算法是可以自定义源点的)到其他各个顶点的最短路径
首先第一步,我们先声明一个dis数组,该数组初始化的值为:
我们的顶点集T的初始化为:T={v1}
既然是求 v1顶点到其余各个顶点的最短路程,那就先找一个离 V1 号顶点最近的顶点。通过数组 dis 可知当前离v1顶点最近是 v3顶点,则 v1顶点到 v3顶点的最短路程就是当前 dis[2]值,将V3加入到T中。
你以为这次循环中已经结束了吗?no,接着,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3是否有出度,发现以v3 为起点的有: < v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比源点v1–v4短,因为dis[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新dis[3]的值,得到如下结果:
因此 distance[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 distance[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。
然后,我们又从distance中查找除了已经访问的V1和V3外的其他点中的最小值,发现distance[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是dis[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组dis的值,v5有两条出度:< v5,v4>和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而dis[3]的值为60,所以我们要更新dis[3]的值.另外,v1-v5-v6的长度为:90,而distance[5]为100,所以我们需要更新distance[5]的值。更新后的dis数组如下图:
然后,继续从dis中选择未确定的顶点的值中选择一个最小的值,发现dis[3]的值是最小的,所以把v4加入到集合T中,此时集合T={v1,v3,v5,v4},然后,考虑v4的出度是否会影响我们的数组dis的值,v4有一条出度:< v4,v6>,然后我们发现:v1–v5–v4–v6的长度为:60,而dis[5]的值为90,所以我们要更新dis[5]的值,更新后的dis数组如下图:
然后,我们使用同样原理,分别确定了v6和v2的最短路径,最后dis的数组的值如下:
因此,从图中,我们可以发现v1-v2的值为:∞,代表没有路径从v1到达v2。所以我们得到的最后的结果为:
V1 to V2 最短距离: 无穷大!
V1 to V3 最短距离: 10--------------------路径是:V1------V3
V1 to V4 最短距离: 60--------------------路径是:V1------V3---V4
V1 to V5 最短距离: 50--------------------路径是:V1------V5
V1 to V6 最短距离: 70--------------------路径是:V1------V4---V3---V6
package com.practice.graph;
/**
* Created by 凌 on 2018/11/29.
* 描述:最短路径算法(一):Dijkstra算法
* https://www.cnblogs.com/wsw-seu/p/8185285.html
* https://blog.csdn.net/qq_35644234/article/details/60870719
*/
public class Dijkstra {
/**
* 源点 0 到顶点 i 的最短路径
*/
private int[] distances;
/**
*邻接矩阵
*/
private int[][] adjacencyMatrix;
/**
* 图的顶点个数
*/
private int vertexNum;
/**
* 图的顶点表示
*/
private String[] vertex;
/**
* 图的边数
*/
private int edgeNum;
/**
* 开始遍历图的起点
*/
private int startVertex;
/**
* 标记结点是否已经遍历
*/
boolean[] visited;
/**
* 从源结点开始访问的最短路径
*/
int[] path;
public Dijkstra(int[][] adjacencyMatrix, int vertexNum, String[] vertex, int edgeNum) {
this.adjacencyMatrix = adjacencyMatrix;
this.vertexNum = vertexNum;
this.vertex = vertex;
this.edgeNum = edgeNum;
this.visited = new boolean[vertexNum];
this.distances = new int[vertexNum];
this.path = new int[vertexNum];
}
/**
* 设置开始遍历图的起点,将字符串转成下标
* @param startVertex
*/
public void setStartVertex(String startVertex){
for (int i = 0; i < vertexNum; i++) {
if (startVertex.equals(vertex[i])){
this.startVertex = i;
break;
}
}
}
public int[] getDistances() {
return distances;
}
/**
* dijkstra最短路径算法
* @param startVertex
*/
public void dijkstra(String startVertex){
setStartVertex(startVertex);
//初始化边距
for (int i = 0; i < vertexNum; i++) {
distances[i] = adjacencyMatrix[this.startVertex][i];
}
visited[this.startVertex]=true;
path[0]=this.startVertex;
//同起点的最短距离
int min_cost;
//权值最小的那个顶点的下标。(求好了)
int min_cost_index=0;
//主循环,除了源点,还剩下n-1个点
for (int i = 0; i < vertexNum-1; i++) {
min_cost = Integer.MAX_VALUE;
// 找出当前未使用的点j的dist[j]最小值
for (int j = 0; j < vertexNum; j++) {
if (!visited[j] && distances[j]
参考:https://blog.csdn.net/qq_35644234/article/details/60870719
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html
二、Floyd算法
1.定义概览: 多源最短路径算法
Floyd算法是解决任意两点间的最短路径的一种算法,可以正确处理有向图或有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包。
时间复杂度是O(n3)
2、算法的思路
Floyd算法其实是一个动态规划算法,有递推公式:d[i][j]=min(d[i][j],d[i][k]+d[k][j]),求源点i到j的最短路径,可能存在一个中间点k,使得,顶点i到k,再到j的路径小于i直接到j的路径。即查到i到j的最短路径,我们可以找一个中间点k,然后变成子问题,i到k的最短路径和k到j的最短路径。也就是说,我们可以枚举中间点k,找到最小的d[i][k]+d[k][j],作为d[i][j]的最小值。
通过Floyd计算图G=(V,E)中各个顶点的最短路径时,需要引入两个矩阵,矩阵S中的元素d[i][j]表示顶点i(第i个顶点)到顶点j(第j个顶点)的距离。矩阵P中的元素path[i][j],表示顶点i到顶点j经过了path[i][j]记录的值所表示的顶点。
假设图G中顶点个数为N,则需要对矩阵D和矩阵P进行N次更新。
初始时,矩阵D中顶点d[i][j]的距离为顶点i到顶点j的权值;如果i和j不相邻,则a[i][j]=∞,矩阵P的值为顶点b[i][j]的j的值。
接下来开始,对矩阵D进行N次更新。第1次更新时,如果”d[i][j]的距离” > “d[i][0]+d[0][j]”(d[i][0]+d[0][j]表示”i与j之间经过顶点V1的距离”),则更新a[i][j]为”d[i][0]+d[0][j]”,更新path[i][j]=path[i][0]。
同理,第k次更新时,如果”d[i][j]的距离” > “d[i][k]+d[k][j]”,则更新d[i][j]为”d[i][k]+d[k][j]”,path[i][j]=path[i][k]。更新N次之后,操作完成!
3、Floyd算法的实例过程
上面,我们已经介绍了算法的思路,如果,你觉得还是不理解,那么通过一个实际的例子,把算法的过程过一遍,你就明白了,如下图,我们求下图的每个点对之间的最短路径的过程如下:
4、算法实现(java实现)
package com.practice.graph;
/**
* Created by 凌 on 2018/12/1.
* 描述:最短路径算法(二):Floyd算法
*/
public class Floyd {
/**
*邻接矩阵
*/
private int[][] adjacencyMatrix;
/**
* 图的顶点个数
*/
private int vertexNum;
/**
* 从源结点开始访问的最短路径
*/
private int[][] path;
public Floyd(int[][] adjacencyMatrix, int vertexNum) {
this.vertexNum = vertexNum;
this.adjacencyMatrix = new int[vertexNum][vertexNum];
this.path = new int[vertexNum][vertexNum];
for (int i = 0; i < vertexNum; i++) {
for (int j = 0; j < vertexNum; j++) {
this.adjacencyMatrix[i][j] = adjacencyMatrix[i][j];
path[i][j] = -1;
}
}
}
/**
* floyd算法,使用动态规划算法
* 有递推公式:d[i][j]=min(d[i][j],d[i][k]+d[k][j])
* d[i][j]表示顶点i到顶点j的最短路径
* @return
*/
public int[][] floyd(){
for (int k = 0; k < vertexNum; k++) {
for (int i = 0; i < vertexNum; i++) {
for (int j = 0; j < vertexNum; j++) {
if (adjacencyMatrix[i][k] temp){
adjacencyMatrix[i][j]= temp;
path[i][j] = k;
}
}
}
}
}
return path;
}
public int[][] getPath() {
return path;
}
public void printPath() {
System.out.println("各个顶点对的最短路径:");
int row = 0;
int col = 0;
int temp = 0;
for (row = 0; row < vertexNum; row++) {
for (col = row + 1; col < vertexNum; col++) {
System.out.printf( "v" + (row + 1) + "---" + "v" + (col+1) + " weight: "
+ adjacencyMatrix[row][col] + " path: " + " v" + (row + 1));
temp = path[row][col];
//循环输出途径的每条路径。
while (temp != col && temp!=-1) {
System.out.printf("-->" + "v" + (temp + 1));
temp = path[temp][col];
}
System.out.println("-->" + "v" + (col + 1));
}
System.out.println("");
}
}
public static void main(String[] args) {
int vertexNum = 7;
int[][] adjacencyMatrix={{Integer.MAX_VALUE, 12, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 16, 14},
{12, Integer.MAX_VALUE, 10, Integer.MAX_VALUE, Integer.MAX_VALUE, 7, Integer.MAX_VALUE},
{Integer.MAX_VALUE, 10, Integer.MAX_VALUE, 3, 5, 6, Integer.MAX_VALUE},
{Integer.MAX_VALUE, Integer.MAX_VALUE, 3, Integer.MAX_VALUE, 4, Integer.MAX_VALUE, Integer.MAX_VALUE},
{Integer.MAX_VALUE, Integer.MAX_VALUE, 5, 4, Integer.MAX_VALUE, 2, 8},
{16, 7, 6, Integer.MAX_VALUE, 2, Integer.MAX_VALUE, 9},
{14, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 8, 9, Integer.MAX_VALUE}};
Floyd floyd= new Floyd(adjacencyMatrix, vertexNum);
int[][] path = floyd.floyd();
floyd.printPath();
}
}
各个顶点对的最短路径:
v1---v2 weight: 12 path: v1-->v2
v1---v3 weight: 22 path: v1-->v2-->v3
v1---v4 weight: 22 path: v1-->v6-->v5-->v4
v1---v5 weight: 18 path: v1-->v6-->v5
v1---v6 weight: 16 path: v1-->v6
v1---v7 weight: 14 path: v1-->v7
v2---v3 weight: 10 path: v2-->v3
v2---v4 weight: 13 path: v2-->v3-->v4
v2---v5 weight: 9 path: v2-->v6-->v5
v2---v6 weight: 7 path: v2-->v6
v2---v7 weight: 16 path: v2-->v6-->v7
v3---v4 weight: 3 path: v3-->v4
v3---v5 weight: 5 path: v3-->v5
v3---v6 weight: 6 path: v3-->v6
v3---v7 weight: 13 path: v3-->v5-->v7
v4---v5 weight: 4 path: v4-->v5
v4---v6 weight: 6 path: v4-->v5-->v6
v4---v7 weight: 12 path: v4-->v5-->v7
v5---v6 weight: 2 path: v5-->v6
v5---v7 weight: 8 path: v5-->v7
v6---v7 weight: 9 path: v6-->v7
参考:https://blog.csdn.net/xianpingping/article/details/79947091
https://blog.csdn.net/qq_35644234/article/details/60875818