<h2 align="center"><strong><em>The Dijkstra Algorithm</em></strong></h2><p align="center"><a target=_blank href="mailto:eryar@163.com"><strong><em>eryar</em></strong>@<strong><em>163</em></strong>.<strong><em>com</em></strong></a> </p><p><strong>摘要</strong>:本文用<strong><em>C</em></strong>实现了图的最短路径<strong><em>Dijkstra</em></strong>算法,并将自己理解该算法的方式与大家分享下,若有错误之处,欢迎指正。 </p><p><strong>关键字</strong>:图、最短路径、<strong><em>Graph</em></strong>、<strong><em>Dijkstra</em></strong> </p><h3>一、引言 <strong><em>Introduction</em></strong></h3><p>对图<strong><em>G</em></strong>中的每一条边<strong><em>e</em></strong>都赋以一个实数<strong><em>w</em></strong>(<strong><em>e</em></strong>),则<strong><em>G</em></strong>连同它边上的权称为赋权图。赋权图经常出现在图论的实际应用问题中,边上的权(<strong><em>Weight</em></strong>)可以表示各种实际意义,如在输送网络中,权可以表示两地的距离,可以表示运输的时间,还可以表示运输的费用等。许多最优化问题相当于在一个赋权图中找出某类具有最小(或最大)权的子图。例如,给出一个连接各城镇的铁路网,要找出一条给定两个城镇间的最短路线。这个问题的图论模型建立如下:以顶点代表城镇,边代表城镇间的铁路线,权表示直接相连的城镇之间的铁路距离,得到一个赋权图,即网络<strong><em>N</em></strong>。 </p><p>本文讨论带权的有向图,并称路径上的第一个顶点为源点(<strong><em>Source</em></strong>),最后一个顶点为终点(<strong><em>Destination</em></strong>)。 </p><p align="center"><a target=_blank href="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/Dijkstra_Animation_4.gif"><img style="DISPLAY: block; FLOAT: none; MARGIN-LEFT: auto; MARGIN-RIGHT: auto" title="Dijkstra_Animation" alt="Dijkstra_Animation" src="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/Dijkstra_Animation_thumb_1.gif" width="283" height="222" /></a> </p><h3>二、算法理解 <strong><em>Understanding the Algorithm</em></strong></h3><p><strong><em>Dijkstra</em></strong>算法描述为:假设用带权邻接矩阵来表示带权有向图。首先引进一个辅助向量<strong><em>D</em></strong>,它的每个分量<strong><em>D</em></strong>[<strong><em>i</em></strong>]表示当前所找到的从始点<strong><em>v</em></strong>到每个终点<strong><em>Vi</em></strong>的最短路径。它的初始状态为:若两顶点之间有弧,则<strong><em>D</em></strong>[<strong><em>i</em></strong>]为弧上的权值;否则置<strong><em>D</em></strong>[<strong><em>i</em></strong>]为无穷大。 </p><p>u 找到与源点<strong><em>v</em></strong>最近的顶点,并将该顶点并入最终集合<strong><em>S</em></strong>; </p><p>u 根据找到的最近的顶点更新从源点<strong><em>v</em></strong>出发到集合<strong><em>V</em></strong>-<strong><em>S</em></strong>上可达顶点的最短路径; </p><p>u 重复以上操作。 </p><p><a target=_blank href="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/clip_image004_2.jpg"><img style="BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; DISPLAY: inline; BORDER-TOP: 0px; BORDER-RIGHT: 0px" title="" border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/clip_image004_thumb.jpg" width="243" height="234" /></a> </p><p>图<strong><em>1</em></strong> 带权有向图 </p><p>以前总是认为<strong><em>Dijkstra</em></strong>算法可以用来求从源点到指定终点的最短路径,导致总不能抓住算法的中心思想。现在认为把握<strong><em>Dijkstra</em></strong>的算法要点为: </p><p>u <strong><em>Dijkstra</em></strong>提出了一个按路径长度递增的次序产生最短路径的算法; </p><p>u 每次循环都可以得到一个从源点到某个顶点的最短路径,某个即不是确定的一个; </p><p>以带权有向图<strong><em>1</em></strong>为例说明<strong><em>Dijkstra</em></strong>算法的执行过程:假设源点为<strong><em>v0</em></strong>,则初始状态时源点到其它各顶点的距离为: </p><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" width="103"><p>源点 终点</p></td><td valign="top" width="86"><p><strong><em>v1</em></strong></p></td><td valign="top" width="95"><p><strong><em>v2</em></strong></p></td><td valign="top" width="95"><p><strong><em>v3</em></strong></p></td><td valign="top" width="95"><p><strong><em>v4</em></strong></p></td><td valign="top" width="95"><p><strong><em>v5</em></strong></p></td></tr><tr><td valign="top" width="103"><p><strong><em>v0</em></strong></p></td><td valign="top" width="86"><p>∽</p></td><td valign="top" width="95"><p><strong><em>10</em></strong></p></td><td valign="top" width="95"><p>∽</p></td><td valign="top" width="95"><p><strong><em>30</em></strong></p></td><td valign="top" width="95"><p><strong><em>100</em></strong></p></td></tr></tbody></table><p>由上表可知,与源点<strong><em>v0</em></strong>最近的顶点为<strong><em>v2</em></strong>,距离为<strong><em>10</em></strong>。 </p><p>将<strong><em>v2</em></strong>加入到最终顶点集合<strong><em>S</em></strong>中。 </p><p>再根据<strong><em>v2</em></strong>更新从源点到其它顶点的最短距离,即从<strong><em>v0</em></strong>-<strong><em>v2</em></strong>-<strong><em>v3</em></strong>的距离为<strong><em>60</em></strong><∽,所以将<strong><em>v0</em></strong>到<strong><em>v3</em></strong>的距离更新为<strong><em>60</em></strong>,如下表所示: </p><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" width="103"><p>源点 终点</p></td><td valign="top" width="86"><p><strong><em>v1</em></strong></p></td><td valign="top" width="95"><p><strong><em>v2</em></strong></p></td><td valign="top" width="95"><p><strong><em>v3</em></strong></p></td><td valign="top" width="95"><p><strong><em>v4</em></strong></p></td><td valign="top" width="95"><p><strong><em>v5</em></strong></p></td></tr><tr><td valign="top" width="103"><p><strong><em>v0</em></strong></p></td><td valign="top" width="86"><p>∽</p></td><td valign="top" width="95"><p><strong><em><del>10</del></em></strong><del></del></p></td><td valign="top" width="95"><p><strong><em>60</em></strong></p></td><td valign="top" width="95"><p><strong><em>30</em></strong></p></td><td valign="top" width="95"><p><strong><em>100</em></strong></p></td></tr></tbody></table><p>由上表可知,与源点<strong><em>v0</em></strong>次近的顶点为<strong><em>v4</em></strong>,距离为<strong><em>30</em></strong>。 </p><p>将<strong><em>v4</em></strong>加入到最终顶点集合<strong><em>S</em></strong>中; </p><p>再根据<strong><em>v4</em></strong>更新从源点到其它顶点的最短距离。即从<strong><em>v0</em></strong>-<strong><em>v4</em></strong>-<strong><em>v3</em></strong>的距离为<strong><em>50</em></strong><<strong><em>60</em></strong>,所以将<strong><em>v0</em></strong>到<strong><em>v3</em></strong>的距离更新为<strong><em>50</em></strong>;从<strong><em>v0</em></strong>-<strong><em>v4</em></strong>-<strong><em>v5</em></strong>的距离为<strong><em>90</em></strong><<strong><em>100</em></strong>,所以将<strong><em>v0</em></strong>到<strong><em>v5</em></strong>的距离更新为<strong><em>90</em></strong>。 </p><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" width="103"><p>源点 终点</p></td><td valign="top" width="86"><p><strong><em>v1</em></strong></p></td><td valign="top" width="95"><p><strong><em>v2</em></strong></p></td><td valign="top" width="95"><p><strong><em>v3</em></strong></p></td><td valign="top" width="95"><p><strong><em>v4</em></strong></p></td><td valign="top" width="95"><p><strong><em>v5</em></strong></p></td></tr><tr><td valign="top" width="103"><p><strong><em>v0</em></strong></p></td><td valign="top" width="86"><p>∽</p></td><td valign="top" width="95"><p><strong><em><del>10</del></em></strong><del></del></p></td><td valign="top" width="95"><p><strong><em>50</em></strong></p></td><td valign="top" width="95"><p><strong><em><del>30</del></em></strong><del></del></p></td><td valign="top" width="95"><p><strong><em>90</em></strong></p></td></tr></tbody></table><p>重复以上操作…… </p><p>直到最终集合包含了所有的顶点。</p>
/*Dijkstra求单源最短路径 2010.8.26*/
#include<fstream>
#include <iostream>
#include<stack>
#define M 100
#define N 100
using namespace std;
typedef struct node
{
int matrix[N][M]; //邻接矩阵
int n; //顶点数
int e; //边数
}MGraph;
void DijkstraPath(MGraph g, int *dist, int *path, int v0) //v0表示源顶点
{
int i, j, k;
bool *visited = (bool *)malloc(sizeof(bool)*g.n);
for (i = 0; i<g.n; i++) //初始化
{
if (g.matrix[v0][i]>0 && i != v0)
{
dist[i] = g.matrix[v0][i];
path[i] = v0; //path记录最短路径上从v0到i的前一个顶点
}
else
{
dist[i] = INT_MAX; //若i不与v0直接相邻,则权值置为无穷大
path[i] = -1;
}
visited[i] = false;
path[v0] = v0;
dist[v0] = 0;
}
visited[v0] = true;
for (i = 1; i<g.n; i++) //循环扩展n-1次
{
int min = INT_MAX;
int u;
for (j = 0; j<g.n; j++) //寻找未被扩展的权值最小的顶点
{
if (visited[j] == false && dist[j]<min)
{
min = dist[j];
u = j;
}
}
visited[u] = true;
for (k = 0; k<g.n; k++) //更新dist数组的值和路径的值
{
if (visited[k] == false && g.matrix[u][k]>0 && min + g.matrix[u][k]<dist[k])
{
dist[k] = min + g.matrix[u][k];
path[k] = u;
}
}
}
}
void showPath(int *path, int v, int v0) //打印最短路径上的各个顶点
{
stack<int> s;
int u = v;
while (v != v0)
{
s.push(v);
v = path[v];
}
s.push(v);
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
}
int main(int argc, char *argv[])
{
int n, e; //表示输入的顶点数和边数
fstream cin;
cin.open("in16.txt");
while (cin >> n >> e&&e != 0)
{
int i, j;
int s, t, w; //表示存在一条边s->t,权值为w
MGraph g;
int v0;
int *dist = (int *)malloc(sizeof(int)*n);
int *path = (int *)malloc(sizeof(int)*n);
for (i = 0; i<N; i++)
for (j = 0; j<M; j++)
g.matrix[i][j] = 0;
g.n = n;
g.e = e;
for (i = 0; i<e; i++)
{
cin >> s >> t >> w;
g.matrix[s][t] = w;
}
cin >> v0; //输入源顶点
DijkstraPath(g, dist, path, v0);
for (i = 0; i<n; i++)
{
if (i != v0)
{
showPath(path, i, v0);
cout << dist[i] << endl;
}
}
}
return 0;
}