一.Dijkstra算法可以解决的问题
在给定的带权有向图中,我们任意指定一个点作为源点,求该源点到其他各点的最短路径时,我们便需要用到Dijkstra算法。
二.Dijkstra算法的实现思想
1.应该用什么数据结构来存储带权有向图?
我们知道,用于存储图的数据结构主要是邻接表和领接矩阵,由于Dijkstra算法需要经常用到边的权值,这里我们采用领接矩阵存储更加好一些。
2.实现Dijkstra算法还需要引入那些数据结构?
(1)in[]t s 用于记录已经走过的点,每当我们找到源点到另一个点的最短路径时,就将该点加入集合s。具体实现时,我们将数组中的所有值初始化为0,每当找到一个点的最短路径,就将该点对应数组的值变为1。
(2)int[] distance 用于记录源点到各个点的最短距离。该数组的值会随着
集合s的更新而更新。
(3)int[] path 用于记录源点到其余各个顶点的关键路径。本质上path[i]记录的是当前结点的上一步骤的点。
(4)全局常量 static int infinite = 10000 表示无穷远。
3.Dijkstra算法的具体实现步骤
(1)根据起始点初始化各个数据结构,包括int[] s,int[] distance,int[] path,起始时由于集合s中只有起始点,所以distance中可能会存在不可达的点,我们将其初始化为“无穷远”。path数组中初始化所有的顶点的上一步为起始点。为了避免在找MIN{distance[i]}时,多次的最小值都是由出发点引出。 其次初始化源点的path值为-1以标志改点为源点。
(2)找出MIN{distance[i]},将其对应的顶点加入集合s。同时更新distance[]数组和s[]数组和path[]数组。
(3)重复(1)(2)两步,直到s[]数组中的值全为-1。
三.java实现Dijkstra算法
eg:如下图求,从顶点v0出发到其余各点的最短路径
import java.util.Arrays;
import java.util.Scanner;
import java.util.Stack;
/*
迪杰斯特拉算法求最短路径
求从某个源点到其余各顶点的最短距离
测试用例:
6 8
0 5 100
0 4 30
0 2 10
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60
*/
public class Dijkstra {
static int infinite = 10000; //将此数据定义为无穷远,意味着两顶点不可达
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int numberOfNode = sc.nextInt(); //存储该图的顶点的总数
int numberOfEdge = sc.nextInt(); //存储该图的边的条数
int[][] tempDate = new int[numberOfEdge][3]; //用于临时存放数据
int[][] date = new int[numberOfNode][numberOfNode];//领接矩阵来存储该图
for(int i=0;i<numberOfEdge;i++) {
tempDate[i][0] = sc.nextInt(); //边的始点
tempDate[i][1] = sc.nextInt(); //边的终点
tempDate[i][2] = sc.nextInt(); //边的权值
}
sc.close();
//稠密数组向稀疏数组转换,即将其转换为领接矩阵存储
for(int i=0;i<numberOfNode;i++) { //初始化领接矩阵
for(int j=0;j<numberOfNode;j++) {
date[i][j] = infinite;
}
}
//稠密矩阵向稀疏矩阵转换,将图存储在领接矩阵内
for(int i=0;i<numberOfEdge;i++) {
date[tempDate[i][0]][tempDate[i][1]] = tempDate[i][2];
}
dijkstra(0,date);
}
//dijkstra算法求最短路径,参数为起始点,和整个领接矩阵
public static void dijkstra(int start,int[][] graph) {
int[] s = new int[graph.length]; //集合s
int[] distance = new int[graph.length]; //用来记录从起始点到其余各点的最短距离
int[] path = new int[graph.length]; //用来记录从起始点到其余各点的最短路径
//从当前所给起始点出发初始化各数据结构
for(int i=0;i<graph.length;i++) {
distance[i] = graph[start][i];
path[i] = start;
}
s[start] = 1; //将起始点加入集合s
path[start] = -1;
int index = minimumIndex(distance,s);
path[index] = start;
while(index != -1) {
s[index] = 1; //将该元素加入集合s
for(int i=0;i<graph.length;i++) { //更新最短距离数组
if(s[i] != 1 && graph[index][i]+distance[index]<distance[i]) {
distance[i] = graph[index][i]+distance[index];
path[i] = index;
}
}
index = minimumIndex(distance,s);
}
//打印最短路径和最短距离
for(int i=0;i<distance.length;i++) {
if(i != start && distance[i] != infinite) {
System.out.print("起始点"+start+"到点"+i+"的最短距离为:"+distance[i]+" 最短路径为:");
//打印最短路径
int pro = path[i];
Stack<Integer> stack = new Stack<>();
while(pro != -1) {
stack.push(pro);
pro = path[pro];
}
while(!stack.isEmpty()) {
System.out.print(stack.pop()+"->");
}
System.out.println(i);
}
if(i != start && distance[i] == infinite) {
System.out.println("起始点"+start+"无法到达点"+i);
}
}
}
//求最短距离所对应的下标
public static int minimumIndex(int[] distance,int[] s) {
int count = 0;
for(int i=0;i<s.length;i++) {
if(s[i] == 0) { //只求距离集合s以外的最短距离
count++;
}
}
if(count == 0) { //如果count的值为-1说明所有的顶点都已经加入了集合s,即已经完成的dijkstar算法
return -1;
}
int[] minimumToS = new int[count];
int j = 0;
for(int i=0;i<distance.length;i++) {
if(s[i] != 1) {
minimumToS[j++] = distance[i];
}
}
Arrays.parallelSort(minimumToS);
for(int i=0;i<distance.length;i++) {
if(distance[i] == minimumToS[0] && s[i] != 1) {
return i;
}
}
return -1;
}
}
代码分析:
1.Dijkstra算法的核心在于distance[]数组的更新
s[i] != 1 && graph[index][i]+distance[index]<distance[i]
distance[i]表示当前起始点到顶点i的最短距离,distance[index] + graph[index][i]表示index顶点加入了集合s后和顶点i的距离。
2.如何理解path数组?
以上即是笔者对Dijkstra算法的简单概述,如有错误还请批评指正。