在力扣刷题,刷到关于动态规划算法的时候。所以找了下相关的学习资料。此篇是继上篇后,讲的是最短路径算法。把自己的学习记录下,也希望能帮助到某些同学
如果不知道什么是dijkstra的?可以点击此处去,视频上讲解的非常清楚,很到位dijkstra最短路径算法
一、使用dijkstra算法,算出最短路径(文字讲解)
在网上找了1题,求出A --> F的最短距离(线上的数字是点到点之间的距离)
1.1、点在A点上
我们画一个辅助图来讲解;起点在A点上,我们看看到各点的情况,用 ∞ 表示2个点不能直接到达
- 点在A点上,A --> A的距离为 0; 毋庸置疑起点就是A点,用 √ 表示此点被遍历
- A --> B的距离为6,B前面的点为A
- A --> C的距离为3,C前面的点为A
- A不能直接到达D,E,F所以为无穷大
我们比较在未遍历的 点到点的距离里找到最小值,此时距离为3,点来到了C点,并把C打上勾
1.2、点在C点上
- C--> B点;此时是A-->C-->B,距离为5,而A-->B点距离为6,距离5更小,更新距离及B前面的点
- C--> D点;此时是A-->C-->D,距离为6,D点前面的点为C
- C--> E点;此时是A-->C-->E,距离为7,E点前面的点为C
继续在未遍历的 点到点的距离里找到最小值,此时距离为5,点来到了B点,并把B打上勾
1.3、点在B点上
- B--> D点;此时是A-->B-->D,距离为11,之前到D点的最短距离是6,不更新
- B--> C点;因为C点已经被遍历,所以也不更新。
继续在未遍历的 点到点的距离里找到最小值,此时距离为6,点来到了D点,并把D打上勾
1.4、点在D点上
- D--> B;因为B点已被遍历,不更新
- D--> C;C点已遍历,不更新
- D--> F;此时是A-->C-->D-->F;距离为9
- D--> E;此时是A-->C-->D-->E;距离为8,比之前7大,不更新
继续在未遍历的 点到点的距离里找到最小值,此时距离为7,点来到了E点,并把E打上勾
1.5、点在E点上
- E--> C;C被遍历,不更新
- E--> D;D被遍历,不更新
- E--> F;此时是A-->C-->E-->F,距离为12,比之前9大,不更新
继续在未遍历的 点到点的距离里找到最小值,此时距离为9,点来到了F点,并把F打上勾。因为之前的点都被遍历了。所以不会更新数据。
最后得到A-->F点的距离为9,根据前面点的属性可以看出,路径为:A-->C-->D-->F
二、用java实现dijkstra算法
首先我们要用一个二维数组,把点到点的关系,和距离表示出来。如图:
- 第一行第一列0:表示A-->A为0
- 第一行第二列6:表示A-->B为6
- 到不了的为无穷大,我们用-1表示。 最终二维数组为:
int[][] adjMatrix = {
{0, 6, 3, -1, -1, -1},
{6, 0, 2, 5, -1, -1},
{3, 2, 0, 3, 4, -1},
{-1, 5, 3, 0, 2, 3},
{-1, -1, 4, 2, 0, 5},
{-1, -1, -1, 3, 5, 0}};
根据上面文字的讲解,也就是帮我们理清楚了逻辑,只要按这个逻辑去实现代码即可,代码如下:
public void myDijikstra(int[][] adjMatrix) {
//根据文字讲解,我们首先需要
//1、点到点的距离 result;-1表示无穷大,即当前2个点不能直接到达
//2、前面点的集合 fontPoints;-1:前面无点 0:A点 1:B点 2:C点 3:D点 4:E点 5:F点
//3、此点是否被遍历 used
int[] result = new int[adjMatrix.length];
int[] fontPoints = new int[adjMatrix.length];
boolean[] used = new boolean[adjMatrix.length];
//1、初始result[0]就是起点A-->A,距离为0
//2、初始化fontPoints[0] = -1;即起点A前面没点
//3、初始化used[0],即起点A被遍历
result[0] = 0;
fontPoints[0] = -1;
used[0] = true;
//因为起点算作已遍历的点,循环从1开始。
for (int i = 1; i < adjMatrix.length; i++) {
//第一个数组表示的就是 A 到各点的集合
result[i] = adjMatrix[0][i];
//能到达说明前面点是A,不能到达设置成-1
if (adjMatrix[0][i] != -1) {
fontPoints[i] = 0;
} else {
fontPoints[i] = -1;
}
//把其他点都设置为未遍历
used[i] = false;
}
//每次到一个点,更新完距离和前面点后,又继续找出 点到点的最短距离;
//除里起点A循环了5次,所以这是第一个for循环;j=1去掉A点
for (int j = 1; j < adjMatrix.length; j++) {
//我们可以先忽略上面的循环,看看一次是怎么找出最短距离的点的
//设置2个标识,最短距离min,当前点的index
int min = Integer.MAX_VALUE;
int index = 0;
//同样除去A点
for (int i = 1; i < result.length; i++) {
//1、!used[i] 此点未被遍历
//2、result[i] != -1 两点间是可以直接到达的
//3、min > result[i] 通过min找出最小值及最小点的index
if (!used[i] && result[i] != -1 && min > result[i]) {
min = result[i];
index = i;
}
}
//将此最小点设置为已遍历
used[index] = true;
//找到了最小点index后,更新最短距离,及前面点
//例如点在A点到各点距离 {0, 6, 3, -1, -1, -1} 找到最短距离3,index=2;然后来到B点 adjMatrix[index]:{3, 2, 0, 3, 4, -1},
//1、!used[i] 该未被遍历的,遍历过的无需更新
//2、adjMatrix[index][i] != -1 该2点是可以直接打到的
//3、如果 result[i] == -1(该点的最短距离是无穷大)
// 或者 result[i] > min + adjMatrix[index][i] 当前的最短距离,小于之前的最短距离,例如A-->B 和A-->C-->B
// 满足以上3点,我们去更新数据
for (int i = 1; i < adjMatrix[index].length; i++) {
if (!used[i] && adjMatrix[index][i] != -1 && (result[i] == -1 || result[i] > min + adjMatrix[index][i])) {
//到达一个新的点时,更新最短距离
result[i] = min + adjMatrix[index][i];
//到达一个新的点时,更新新点前面的点
fontPoints[i] = index;
}
}
}
}
得到result和fontPoints后,我们分别打印下;result如下;和我们上面说的文字讲解的时候,得到的数值是一样的。
我们打印下fontPoints,如下:
这里什么意思呢稍微介绍下,
- 集合的index和集合里的value值都表示--> 0:A点 1:B点 2:C点 3:D点 4:E点 5:F点
- 比如我们要得到F点的路径,把index=5带入 5后面得到3,用3得到2,用2得到0。即 5>3>2>0,路径为F--D--C--A,因为是前面的点所以路径为:A-->C-->D-->F
我们也可以用一个函数表示,带入index即可得到每个点的路径,函数如下:
//打印某个点的路径
//1、fontPoints 前面点的集合,也就是文字讲解里前面的点
//2、index 表示打印第几个点的路径
public void logAll(int[] fontPoints, int index) {
if (index == -1) {
return;
}
Log.e("看看当前路径",index+"");
logAll(fontPoints,fontPoints[index]);
}