算法之单源最短路径

简介: 算法之单源最短路径

一、引言


> 上次两个算法是否沉淀了呢? 这次我们换一种,我们来学会偷懒~

> 单源最短路径(Dijkstra算法),将偷懒进行到底~

> 大家有兴趣,可以将三个算法对比分析一下。

 

二、小小问题


* Q:如图,小明(标号为A)到其他同学的最短距离是多少呢?(注意箭头方向)


image.png


看着这个图是不是一脸蒙呢? 注意箭头~~


image.png


Q:两点之间如何存储边的距离呢?

比如B->A的长度为2 length[B][A]=2 这样是不是不错? 所以我们可以得到下面的表格了。∞ 代表不能到达


image.png


那么问题来了

Q:如果用Java来解决,我们输入这些点,这些边的数据,能不能得出A点到其他点的最短距离呢?


通过二维表格我们可以知道从A点初始时到各个点的距离(不一定是最短的距离):


image.png


我们根据这个例子,将这个走一遍看看怎么做;


  • 先找距离A最近的顶点, 通过上面的表格可以知道离A最近的是C点。当选择了C点,AC的最短距离就确定了,至于为什么,你们能说出来吗? 因为C点是距离A点最近的点,因为边的长度为正数,所以A可能通过其他点到C,但是长度没C的短。


  • 选择了C,C有两条出路:(C->B)(C->F) .这怎么算呢? 先判断(A->C)+(C->B)这条路能否比(A->B)的路程短 。然后比较发现(A->C)+(C->B) <(A->B) 因此更新 length[A][B] =19;  然后判断(A->C)+(C->F)这条路能否比(A->F)的路程短,之后发现(A->C)+(C->F)< (A->F) 因此更新length[A][F]=12

 

更新完后的长度为:


image.png


  • 然后继续在除了C以外的点,找距离A点最近的点。通过上面的表格,我们可以知道接下来最近的顶点是F。当选择了F点,(A->F)的最短距离就确定了,至于为什么?能说出来吗?


  • 选择了F,F有两条出路:(F->D)(F->E),同样的,怎么算呢?先判断(A->F)+(F->D)这条路是否比(A->D)的路程短,之后比较发现((A->F)+(F->D))(长度25)< (A->D)(长度28,因此更新length[A][D]=25;然后判断(A->F)+(F->E)是否比(A->E)路程短,之后发现(A->F)+(F->E)<(A->E). 因此更新length[A][E]=30;


更新完后的长度为:


image.png


  • 然后继续在除了C,F以外的点,找距离A点最近的点。通过上面的表格,我们知道最近的顶点是B。当选择了B(A->B)的最短距离就确定了,再问一遍,是为什么呢?


  • 选择B,B有两条出路:(B->E)(B->A),同样的,怎么算呢?先判断(A->B)+(B->E) 这条路是否比(A->E)的路程短。然后比较发现((A->B)+(B->E))(29) < A->E(30),因此更新length[A][E]=29.之后判断(A->B)+(B->A)是否比(A->A)的路程短。然后发现(A->B)+(B->A)>(A->A); 因此更新length[A][A] =0(其实不需要更新)


更新完后的长度为:


image.png


  • 然后继续在除了C,F,B以外的点,找距离A点最近的点。通过上面的表格,我们知道最近的点是D。当选择了DA->D的最短距离就确定了。选择D,不需要做任何处理。继续找距离A点最近的点,接下来最近的点是E;E只有一条出路:E->D;先判断A->D+E->D这条路是否比A->D的路程短。然后比较发现((A->E)+(E->D))(长度33) > (A->D)(29)


更新完后的长度为:


image.png


  • 接着找距离A点近的,但是发现所有的点都已经找过,最短距离已经计算完毕。

 

思考:这个过程的代码如何写呢?似乎就是循环(这层循环点)里面嵌套循环(找寻与起始点距离近的点),然后循环找点(距离近的点到其他点的距离)然后判断距离是否为最短。


让我们试下

package dijkstra;
import java.util.Scanner;
/**
* Created by Administrator on 2018/5/20.
* target: Dijkstra算法示例
* 输入:
6 9
0 1 1
0 2 12
1 2 9
1 3 3
2 4 5
3 2 4
3 4 13
3 5 15
4 5 4
* 输出:
* 0 1 8 4 13 17
*
* @author danqiusheng
*/
public class Dijkstra {
   static int INF = Integer.MAX_VALUE;//无法到达
   public static void main(String[] args) {
       Scanner read = new Scanner(System.in);
       // 输入points, edges 顶点数和边树
       int points = read.nextInt();
       int edges = read.nextInt();
       int[][] length = new int[points][points];
       // 初始化边(当前点到当前点为0)
       for (int i = 0; i < points; i++) {
           for (int j = 0; j < points; j++) {
               if (i == j) length[i][j] = 0;
               else length[i][j] = INF;
           }
       }
       read.nextLine();
       //输入顶点关系
       for (int i = 0; i < edges; i++) {
           String[] data = read.nextLine().split(" ");
           length[Integer.parseInt(data[0])][Integer.parseInt(data[1])] = Integer.parseInt(data[2]);
       }
       // 用一个一维数组存储当前点到其他点的距离,因为要根据二维表获取点到点的距离,不更改二维表的数据
       int[] distance = new int[points];
       // 初始化1点点到其余各个顶点的初始路程
       for (int i = 0; i < points; i++) {
           distance[i] = length[0][i];
       }
       // 定义一个flag数组。这个数组标记距离1最近的点被选择的数据
       boolean[] flag = new boolean[points];
       flag[0] = true;// 1点是最近的点,默认选择
       int min;
       int current = 0;
       for (int i = 0; i < points; i++) {
           min = INF;
           // 找寻距离1最短的点
           for (int j = 0; j < points; j++) {
               if (!flag[j] && distance[j] < min) {
                   min = distance[j];
                   current = j;
               }
           }
           // 设置这个距离1最短的点被选择
           flag[current] = true;
           // 获取这个最短的点到其他点的距离
           for (int m = 0; m < points; m++) {
               if (length[current][m] < INF) { // 判断是否为不可达
                   if (distance[m] > distance[current] + length[current][m]) {// 判断是否小于最短路径
                       distance[m] = distance[current] + length[current][m];
                   }
               }
           }
       }
       //输出最终的结果
       for (int i = 0; i < points; i++) {
           System.out.print(distance[i]+" ");
       }
   }
}


三、概念

>单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。 Dijkstra提出按各顶点与源点v间的路径长度的递增次序,生成到各顶点的最短路径的算法。既先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从源点v 到其它各顶点的最短路径全部求出为止。

 

四、总结

> 这个算法,看起来很简单,但是需要理解。参考书《啊哈,算法》,这本书讲算法不错。

> 上面的示例代码并不是最优的算法,相信大家看出来里面的时间复杂度是O(N*N),在哪里可以优化,这里我先留个坑,日后来填~

 

 

五、小试牛刀

> 题目要求的比较多,我就不贴出来,在git上。

 

- [Question](https://github.com/danqiusheng/algorithm_practice/blob/master/src/dijkstra/Question1.md)

 

 

六、示例代码


- [DijkstraDemo1](https://github.com/danqiusheng/algorithm_practice/blob/master/src/dijkstra/DijkstraDemo1.java)


目录
相关文章
|
7月前
|
算法
Floyd 最短路径【学习算法】
Floyd 最短路径【学习算法】
77 0
|
2月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
92 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
4月前
|
算法 定位技术
路径规划算法 - 求解最短路径 - A*(A-Star)算法
路径规划算法 - 求解最短路径 - A*(A-Star)算法
147 1
|
4月前
|
自然语言处理 算法
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
67 0
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
|
5月前
|
算法 Java
Java语言实现最短路径算法(Shortest Path)
Java语言实现最短路径算法(Shortest Path)
70 3
|
4月前
|
算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
103 0
|
6月前
|
存储 算法 C语言
数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)
数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)
112 1
|
6月前
|
算法 Java
Java数据结构与算法:最短路径算法
Java数据结构与算法:最短路径算法
|
6月前
|
算法 Java 定位技术
Java数据结构与算法:贪心算法之最短路径
Java数据结构与算法:贪心算法之最短路径
|
6月前
|
算法 定位技术
探寻最短路径之谜:Dijkstra算法详解
探寻最短路径之谜:Dijkstra算法详解