前言
在上一篇文章中,我们介绍了prim算法——一种求最小生成树的算法。本篇我们介绍一下Dijkstra算法,什么是Dijkstra算法?其功能呢,是用于求加权连通图中某个点(这里特指一个点哦)到其他所有点的最短路径(也就是该点到其他点的所有路径中,路径权值之和最小的那条路径),该算法计算的便是单源点最短路径。
一、算法思想分析
其实Dijkstra算法和prim算法其思想上有些类型,都是从单源点出发,但这两种算法计算权值的方式是不同的。
明确一下问题:在加权连通图中,我们从某一个点出发,寻找其他点到该点的最短路径及最短路径的距离。
我觉得,Dijkstra算法最巧妙的地方就在于我们每一轮就可以找到一个点到我们指定点的最短路径及距离,而这样的轮数,我们只需要进行n-1次(n个点的连通图)。我们从源点出发,这里假设源点是a点,并且假设总共有5个点,即a、b、c、d、e五个点。每一轮我们都寻找其他四个点是否可达a点,如果可达,那么距离是多少?求出当前轮所有可达路径中,最短的那条,本轮我们就完成了一条最短路径了。而我们再思考一下, 既然我们当前轮可达的距离都已经是最小了,那么我们往后面走,剩下的几轮找到的可达路径的距离,可能会小于我们当前轮找到的这条路径的距离么? 这里需要大家仔细思考一下,这里也是Dijkstra算法一个挺核心的点,想通了这点,Dijkstra算法基本上就算理解了。笔墨能表达的东西有限,如果大家有任何问题可私信或留言,我将尽力解决大家的疑惑。
那么,为了更好的理解,我们来带大家走一遍全过程,先是理论是的一遍,然后是用实际题目代入来讲解。
- 首先,我们从指定的源点出发,假设当前为第一轮。
- 我们寻找一些可以抵达源点的其他顶点(第一轮中,只有直接抵达,如果不是第一轮,那么间接抵达也是可以的,毕竟我们找的是路径,但是间接抵达中,其中间路径包含的点只能是我们前几轮已经找到最短路径顶点!),计算这些可以抵达源点的点到源点的距离,寻找一条距离最短的路径,那么该点到源点的最短路径及距离我们就已经在本轮找到了。那么接下来,我们就需要将该点标记一下,标记为已经找到最短路径的点,可以成为中间点。
- 而接下来寻找的可达源点的点,就可以通过这些中间点来间接抵达。接下来重复第二步,直到我们所有点到源点的最短路径及距离找到即可,而总轮数其实只有n-1轮。
接着,我们举一个栗子来讲一下:
对于图中的b(a,3),其中b是当前可达源点的顶点,a则是这个b顶点的前驱节点,b到最短可达路径的距离。而如果括号内第一个为‘-’,则表示不可达,如果第二个为无穷,也表示不可达。
二、算法效率分析
前面我分析过prim算法,同样的,Dijkstra算法的算法效率依赖于图本身的结构,也依赖于实现图的数据结构(邻接矩阵是一种)。以下是《算法设计与分析基础》一书中的分析:
过于高深,大家可参看一下。
而我是从程序本身实现来看的,算法效率基本上和prime差不多,介于O(n²)-O(n³)之间。
三、算法代码
C语言
在我的C语言代码中,我的输入用的是邻接矩阵,而输出则是一个回溯路径,其中我还将建表过程做了一个中间输出,大家可根据需求进行增减。一些需要注释的东西,代码中都已经备注好。如果有什么错误,还望大家指出。
/* Dijstar算法 在一个连通图中 ,求某个顶点到其他顶点的全部最短路径及举例 */
/*
输入:
邻接矩阵维度:n
起始顶点序号(1-n)
邻接矩阵:(0表示不可达)
输出:指定顶点到其他顶点的最短路径及距离
例如:
输入:
维度:5
指定顶点:1
邻接矩阵:
0 3 0 7 0
3 0 4 2 0
0 4 0 5 6
7 2 5 0 4
0 0 6 4 0
输出:
1:1; 0
2:1->2; sum=3
3: 1->2->3; sum=7
4: 1->2->4; sum=5
5: 1->2->4->5; sum=9
*/
#include<stdio.h>
#define MAXN 1000
#define MaxLen 1000000
int input[MAXN][MAXN];
// int sign[MAXN]= {0}; // 标记sign数组 初始化全为0 ,1表示已经加入已选列表
struct nodeInfo {
int frontNode; // 前驱节点
int sumLen; // 迄今为止距离起始点的最短长度
int haveIn; // 是否在已选节点中
};
nodeInfo sign[MAXN];
void Dijstar(int n