0x00.绪论 Dijkstra算法 算是图论当中最为基础的算法之一,也是各类信息学竞赛( Olympiad in Informatics )当中各大图论算法的基础,碰巧今天的离散数学课刚好讲到了Dijkstra算法,所以作为一名蒟蒻·前OIer,今天就来简单讲讲什么是Dijkstra算法XD
不过我当年打OI的时候基本没怎么研究过图论,所以没出过啥成绩XD
0x01.什么是Dijkstra算法? 定义
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉 于1959 年提出的,因此又叫狄克斯特拉算法 。是从一个顶点到其余各顶点的最短路径 算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法 的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
百度百科:Dijkstra算法
戴克斯特拉算法设置了一顶点集合S ,在集合中所有的顶点与源点s 之间的最终最短路径权值均已确定。算法反复选择最短路径估计最小的点u∈V-S 并将加入中。该算法常用于路由其他图算法的一个子模块
维基百科:戴克斯特拉算法
简单地说,Dijkstra算法就是:在正权图中使用贪心算法不断进行搜索从而求出最短路径的搜索算法
具体过程 在维基百科上有这样一张很经典的动图,很好地向我们演示了什么是Dijkstra算法:
从上面我们不难看出Dijkstra算法的整个过程:
注意:第二步的第一次搜索恒为起始点本身,即将起始点纳入点集
以上即为基本的Dijkstra算法的整个过程,我们不难看出这一种不断搜索最短距离的过程其实是一种类似于贪心算法的思想,同时这样的一种路径结果记录的方法也有一定的动态规划的思想在内——到达被纳入点的最短路径已经是最优状态,无后效性,具有最优子结构
基本概念:图的表示 在图论当中我们常用一个二维矩阵来表示一个图,比如说上面动图中的那一张图:
我们可以使用下面这样的一个矩阵来表示(无向图):
对于无向图而言,他的邻接矩阵一定会是对称的,在离散数学课上已经讲过,这里就不再多说了
我们不难看出,这样一个矩阵很像一个二维数组,因此我们可以使用一个二维数组(邻接矩阵) 表示一张图:
但是毫无疑问地,随着图的越来越大,二维数组所占用的空间也会爆炸式增长(尤其是像OI那样动不动就10^8、10^9的),因此我们还需要更好的方法来表示一张图
对于图G=(V,E) (其中V为点集,E为边集),我们也可以使用邻接表 来表示一张有向图:
1 2 3 4 #include <vector> using namespace std;define edge pair<int , int > vector < pair < int , edge > > map;
当图为稀疏图、顶点较多,即图结构比较大时,更适宜选择邻接表作为存储结构。当图为稠密图、顶点较少时,或者不需要记录图中边的权值时,使用邻接矩阵作为存储结构较为合适。
代码实现 邻接矩阵版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <iostream> using namespace std;#define MAX 10000 void dijkstra (void ) ;void pathOut (int node) ;int map[MAX][MAX]={0 };int node, start;int dist[MAX]={0 };int path[MAX]={0 };bool visited[MAX]={0 };int main (void ) { cout<<"Please enter the number of the nodes" <<endl; cin>>node; cout<<"please enter this maps\'s Adjacency Matrix" <<endl; for (int i=1 ;i<=node;i++) { for (int j=1 ;j<=node;j++) { cin>>map[i][j]; } } cout<<"Please enter the start node: " ; cin>>start; dijkstra (); for (int i = 1 ;i<=node;i++) { cout<<endl<<"distance to node " <<i<<" is " <<dist[i]<<" . The path is: " ; pathOut (i); } return 0 ; } void dijkstra (void ) { for (int i=1 ;i<=node;i++) { dist[i] = MAX; visited[i] = false ; } dist[start] = 0 ; visited[start] = true ; path[start] = start; int best = start; for (int i=1 ;i<=node;i++) { int min = MAX; for (int j = 1 ;j<=node;j++) if (!visited[j]&&dist[j]<min) { min = dist[j]; best = j; } visited[best] = true ; for (int j=1 ;j<=node;j++) if (map[best][j]!=0 &&!visited[j]&&dist[j]>dist[best]+map[best][j]) { dist[j] = dist[best]+map[best][j]; path[j] = best; } } } void pathOut (int node) { if (path[node] != node) { pathOut (path[node]); cout<<" -> " <<node; } else { cout<<node; } }
我们以基本概念中的图作为输入样例,选取点2为出发点:
得到如下结果:
对比原图,我们不难看出我们所得出的结果是正确的
邻接表 方便起见,我们假设起始点恒为点1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <iostream> #include <vector> #include <algorithm> using namespace std;#define edge pair < int, int> #define infinite 1145141919 vector < pair < int , edge > > G; vector<int > key, rel; int node, edg;void init_keys () { key.resize (node); key[0 ]=0 ; for (int i=1 ; i<node; i++) { key[i]=infinite; } } void dijkstra () { rel.resize (node); int count=0 ; sort (G.begin (), G.end ()); while (count<edg) { if ((key[G[count].first]+G[count].second.second)<key[G[count].second.first]) { rel[G[count].second.first]=G[count].first; key[G[count].second.first]=key[G[count].first]+G[count].second.second; } count++; }; } int main () { int x, y, cost; cin>>node>>edg; for (int i=0 ; i<edg; i++) { cin>>x>>y>>cost; G.push_back (pair <int , edge>(x-1 , edge (y-1 , cost))); } init_keys (); dijkstra (); cout<<"Shortest path: \n 1 is ROOT\n" ; for (int i=1 ; i<node; i++) { cout<<"( " <<i+1 <<", " <<rel[i]+1 <<" )\n" ; } cout<<"Shortest path for? : " ; cin>>x; cout<<"\nShortest path from 1 to " <<x<<" is " <<key[x-1 ]; return 0 ; }
优化:堆优化
咕了,下次再补