1.什么是最短路径?
在非网图中,最短路径是指两顶点之间经历的边数最少的路径。
比如这个图,AE就是最短路径

在网图中,最短路径是指两顶点之间经历的边上权值之和最短的路径。
而在这个图中,ADCE是最短路径

2.Dijkstra算法
迪杰斯特拉(Dijkstra)提出了一个按路径长度递增的次序产生最短路径的算法——Dijkstra算法。
2.1基本思想:
设置一个集合S存放已经找到最短路径的顶点,S的初始状态只包含源点v,对vi∈V-S,假设从源点v到vi的有向边为最短路径。以后每求得一条最短路径v, …, vk,就将vk加入集合S中,并将路径v, …, vk , vi与原来的假设相比较,取路径长度较小者为最短路径。重复上述过程,直到集合V中全部顶点加入到集合S中。

我想有的同学肯定读不懂,下面我同样用一个实例来解释Dijkstra算法:
比如我们要求在这个图中,从A点到所有点的最短路径

那么首先,我们将A放入集合S中,从A点开始寻找到每个点的距离,从A->C没有直接路径可以到达,因此距离为无穷,然后我们寻找路径最短的点,很明显从A->B路径为10最短,因此我们将B放入集合S中,这意味着我们已经找到了从A到B的最短路径,接下来我们需要从B点出发寻找最短路径

这时我们发现从B点可以到C点,距离为50,然后我们需要比较从A->B->C的长度和从A->C的长度,如果从A->B->C的长度小于从A->C的,那么我们就替换掉,否则还是维持之前的路径不变,很明显60<无穷,因此我们替换掉之前的A->C无穷,B到D和E没有路径,因此不用比较(在编程时路径为无穷,比较后还是路径不会变化),而A点已经加入集合S当中,不需要比较。
比较完成后我们寻找最短路径,发现A->D路径为30最短,因此我们将D点加入集合S中,下次将从D点开始寻找最短路径

好了,接下来我们要从D点出发了,D点到C和E均有路径,我们比较从A->D->C和A->C的长度,发现A->D->C更近,因此替换掉之前的路径,然后比较A->D->E和A->E的路径,100>90,同样替换掉之前的路径。比较完成后寻找最短路径,下次将从C出发寻找路径,同样将C加入集合S中去

C与E结点相连,距离为10,我们比较A->C->E和A->E的距离,发现50+10<100,因此从A->C->E显然比从A->E要近得多,因此替换掉,由于就剩这一个了,所以不用比较,将E加入集合S中,算法结束,我们便找到了从A出发到每个结点得最短路径!


2.2 Dijkstra算法——伪代码
- 初始化数组dist、path和s;
- while (s中的元素个数 2.1 在dist[n]中求最小值,其下标为k;
2.2 输出dist[j]和path[j];
2.3 修改数组dist和path;
2.4 将顶点vk添加到数组s中;
2.3 代码实现
同样我们需要先进行图的初始化工作
const int MaxSize=100;
const int MAX=10000;
struct Path
{
string route[MaxSize];
int t;
};
template<class DataType>
class MGraph
{
public:
MGraph();
~MGraph(){}
void DFSTraverse(int v);
void BFSTraverse(int v);
void Dijkstra(int v);
void Floyd();
void PrintfGraphAL();
int vertexNum,arcNum;
double dist[MaxSize];
double Dist[MaxSize][MaxSize];
Path path[MaxSize];
DataType vertex[MaxSize];
private:
int arc[MaxSize][MaxSize];
bool DFSvisited[MaxSize];
bool BFSvisited[MaxSize];
};
template<class DataType>
MGraph<DataType>::MGraph()
{
int i,j;
fstream fcin_vertex;
fcin_vertex.open("vertex.txt",ios::in);
vertexNum=0,arcNum=0;
while(!fcin_vertex.eof())
fcin_vertex>>vertex[vertexNum++];
fcin_vertex.close();
memset(DFSvisited,0,sizeof(DFSvisited));
memset(BFSvisited,0,sizeof(BFSvisited));
for(i=0;i<vertexNum;i++)
for(j=0;j<vertexNum;j++)
{
if(i==j)
arc[i][j]=0;
else
arc[i][j]=MAX;
}
fstream fcin_arc;
fcin_arc.open("arc.txt",ios::in);
i=0,j=0;
while(i!=-1)
{
fcin_arc>>i;
if(i==-1)
continue;
else
{
fcin_arc>>j;
if(arc[i][j]==0||arc[i][j]==MAX)
arcNum++;
fcin_arc>>arc[i][j];
arc[j][i]=arc[i][j];
}
}
fcin_arc.close();
}
然后是Dijkstra算法:
template<class DataType>
void MGraph<DataType>::Dijkstra(int v)
{
int i,j,num,b;
bool S[MaxSize];
for(i=0;i<vertexNum;i++)
{
dist[i]=arc[v][i];
S[i]=0;
path[i].t=0;
path[i].route[path[i].t]=vertex[v];
if(dist[i]!=MAX)
path[i].route[++path[i].t]=vertex[i];
}
S[v]=1;
for(num=1;num<vertexNum;num++)
{
double min=MAX;
int k=v;
for(j=0;j<vertexNum;j++)
if(S[j]==0&&dist[j]!=0&&dist[j]<=min)
{
k=j;
min=dist[j];
}
S[k]=1;
for(j=0;j<vertexNum;j++)
if(S[j]==0&&arc[k][j]<MAX)
{
if(dist[j]>dist[k]+arc[k][j])
{
dist[j]=dist[k]+arc[k][j];
for(b=0;b<=path[k].t;b++)
path[j].route[b]=path[k].route[b];
path[j].t=path[k].t;
path[j].route[++path[j].t]=vertex[j];
}
}
}
}
3.Floyd算法
3.1 基本思想
对于从vi到vj的弧,进行n次试探:首先考虑路径vi,v0,vj是否存在,如果存在,则比较vi,vj和vi,v0,vj的路径长度,取较短者为从vi到vj的中间顶点的序号不大于0的最短路径。在路径上再增加一个顶点v1,依此类推,在经过n次比较后,最后求得的必是从顶点vi到顶点vj的最短路径。
比如下面这个有向网图,我们来寻找任意两点间得最短距离:

首先是初始化,标记出开始是相邻两点间得路径和长度

从a开始第一次迭代,我们发现从c->b的长度和从c->a->b要大,因此把路径替换为cab,长度为7(之前为无穷),而从b->c的距离为2,比从b->a->c要短的多,因此不换

同理,从b开始第二次迭代,将a->c替换为a->b->c

从c开始第三次迭代,将b->a替换为b->c->a

这样我们便找到了任意两点间的最短路径和长度!
3.2 代码实现
template<class DataType>
void MGraph<DataType>::Floyd()
{
int i,j,k;
for(i=0;i<vertexNum;i++)
for(j=0;j<vertexNum;j++)
Dist[i][j]=arc[i][j];
for(k=0;k<vertexNum;k++)
4. 完整代码附录如下:(需要读取两个txt文件,分别存放点信息和边信息)
#include
using namespace std;
#include
#include
#include
#include
const int MaxSize=100;
const int MAX=10000;
struct Path
{
string route[MaxSize];
int t;
};
template<class DataType>
class MGraph
{
public:
MGraph();
~MGraph(){}
void DFSTraverse(int v);
void BFSTraverse(int v);
void Dijkstra(int v);
void Floyd();
void PrintfGraphAL();
int vertexNum,arcNum;
double dist[MaxSize];
double Dist[MaxSize][MaxSize];
Path path[MaxSize];
DataType vertex[MaxSize];
private:
int arc[MaxSize][MaxSize];
bool DFSvisited[MaxSize];
bool BFSvisited[MaxSize];
};
template<class DataType>
MGraph<DataType>::MGraph()
{
int i,j;
fstream fcin_vertex;
fcin_vertex.open("vertex.txt",ios::in);
vertexNum=0,arcNum=0;
while(!fcin_vertex.eof())
fcin_vertex>>vertex[vertexNum++];
fcin_vertex.close();
memset(DFSvisited,0,sizeof(DFSvisited));
memset(BFSvisited,0,sizeof(BFSvisited));
for(i=0;i<vertexNum;i++)
for(j=0;j<vertexNum;j++)
{
if(i==j)
arc[i][j]=0;
else
arc[i][j]=MAX;
}
fstream fcin_arc;
fcin_arc.open("arc.txt",ios::in);
i=0,j=0;
while(i!=-1)
{
fcin_arc>>i;
if(i==-1)
continue;
else
{
fcin_arc>>j;
if(arc[i][j]==0||arc[i][j]==MAX)
arcNum++;
fcin_arc>>arc[i][j];
arc[j][i]=arc[i][j];
}
}
fcin_arc.close();
}
template<class DataType>
void MGraph<DataType>::DFSTraverse(int v)
{
int j;
cout<<vertex[v]<<" ";
DFSvisited[v]=1;
for(j=0;j<vertexNum;j++)
if(arc[v][j]!=0&&arc[v][j]!=MAX&&DFSvisited[j]==0)
DFSTraverse(j);
}
template<class DataType>
void MGraph<DataType>::BFSTraverse(int v)
{
int Q[MaxSize];
int front=-1,rear=-1,j;
cout<<vertex[v]<<" ";
BFSvisited[v]=1;
Q[++rear]=v;
while(front!=rear)
{
v=Q[++front];
for(j=0;j<vertexNum;j++)
if(arc[v][j]!=0&&arc[v][j]!=MAX&&BFSvisited[j]==0)
{
cout<<vertex[j]<<" ";
BFSvisited[j]=1;
Q[++rear]=j;
}
}
}
template<class DataType>
void MGraph<DataType>::Dijkstra(int v)
{
int i,j,num,b;
bool S[MaxSize];
for(i=0;i<vertexNum;i++)
{
dist[i]=arc[v][i];
S[i]=0;
path[i].t=0;
path[i].route[path[i].t]=vertex[v];
if(dist[i]!=MAX)
path[i].route[++path[i].t]=vertex[i];
}
S[v]=1;
for(num=1;num<vertexNum;num++)
{
double min=MAX;
int k=v;
for(j=0;j<vertexNum;j++)
if(S[j]==0&&dist[j]!=0&&dist[j]<=min)
{
k=j;
min=dist[j];
}
S[k]=1;
点信息txt文件如下,命名为vertex
西街 公寓楼 鸿远楼 修远楼 明远楼 逸夫图书馆 行政楼 南门 树慧园 天行健 澡堂 北门 操场 仙女楼
校医院 东门
边信息txt文件如下,命名为arc
0 1 50
0 2 80
1 2 100
1 3 200
2 4 200
3 4 100
3 5 150
4 5 70
4 6 70
5 6 100
5 8 300
6 7 50
8 9 100
8 10 70
8 13 50
9 10 70
10 12 50
10 13 150
12 11 150
12 13 50
13 14 300
13 15 100
-1
点和边信息大家也可自行定义
5.运行结果如下:

