Dijkstra算法是用于求解单源(一个起点)最短路径(距离或路径方案)的经典算法。注意:这里所有路径的距离必须非负,否则不能用迪杰斯特拉算法。
从该算法中,我感悟到编程的思想,或者说,解决问题的思想,就是要一步一步来,确定好要做的事情,和做事的顺序,从而少做无用功(或者说本可以省去的活),这里的关键是我们要找到问题各个对象之间隐含的关系(如最典型的关系:可递推或其他)。如在一个图(无向图或者有向图)中求解从一点s到其他各点的最短路径时,就可以先看从该点直接可达的点之间的距离,选择距离最近的点,设为t1。该点t1一定是最终状态,也就是说,从s到达t1的最短路径距离一定是刚才确定的距离。因为再找其他的中间点到达t1一定不可能比这个更小了。(前面已经说过t1是直接可达的点里最近的点了,不妨假设一个中间点u,从s到u再到t1可达,那么从s到u就已经大于等于前面直接从s到t1的距离了,何况还要再加上一个u到t1的距离(非负)。因此,t1是确定的一个s到t1的最短距离的点了)然后以t1为中间点去更新其他还未确定最短路径的点,接着选取距离最短的点,依次类推...直到确定从源点达到所有点的最短路径。
那么,如何用代码描述呢?也就是如何写代码呢?先来理一下代码的主体思路,我们需要确定n个点的最短路径,因此外层循环n次,对循环内,每次选取一个距离最近的点作为中间点是一步,下一步还要以该中间点更新其他还未确定的从源点到各点的最短距离。所以,整体思路就是,外层循环处理n个点,然后里面有两步,分别是,选中间点和更新其他各点距离。
那么需要什么数据结构呢?如何记录一个点是否已经确定了最短路径呢?使用一个bool型的数组vis[]记录即可。如何记录一个点确定的最短路径距离呢?使用用int型的数组dis[]记录即可。其中两个数组的下标对应图中各点的编号。
//初始时dis[]数组中的距离设为INF
void Dijkstra(int s){
dis[s] = 0;
for(int i=0; i<n; i++){ //外层循环
//下面为循环内的第一步,选中间点
int u=-1;
int minv = INF;
for(int j=0; j<n; i++){
if(vis[j] == false && dis[j] < minv){
minv = dis[j];
u = j;
}
}
if(u == -1) return;
vis[u] == true;
//下面为循环内的第二步,更新其他点的距离
for(int k=0; k<adj[u].size(); k++){
edge tempv = adj[u][k];
if(vis[tempv.v] == false && dis[u] + tempv.w < dis[tempv]){
dis[tempv] = dis[u] + tempv.w;
}
}
}
}
简言之,迪杰斯特拉算法的关键就是明白,各个击破的道理,知道:找到从源点直接可达的所有点中距离最短的那个点和相应距离,它一定是整个图中从源点到那个点的最短路径解,从而击破一个点(,然后从源点到其他各点的路径可由前面确定了最短路径的点传递过来得到最短路径,再接着一个一个击破其他点。)
再强调一下,充分利用隐含关系往往是更高效地解决问题的关键。