在一个无权图中,若从一个顶点到另一个顶点存在着一条路径(仅限于无回路的简单路径),则该路径上的边数即为路径长度,等于该路径上的顶点数减一。
从一个顶点到另一个顶点所有可能的路径中,路径长度最短(即经过的边数最少)的路径称为最短路径,其路径长度叫做最短路径长度或最短距离。
在一个带权图中,从一个顶点v到另一个顶点u的路径上所经过各边上的权值之和即为该路径的带权路径长度,从v到u可能不止一条路径,把带权路径长度最短(即其值最小)的那条路径称作是最短路径,其权值之和称作最短路径长度或最短距离。
Dijstra算法用于求图中一个顶点到其余各顶点的最短路径。
算法设置
算法步骤:
代码实现:
void Dijkatra(MGraph &G, int u, int dist[], int path[]){
int S[maxvertexnum]; //S为已求最短路径的顶点集合
for(int i=0; i<G.vexnum; i++)S[i]=0; //集合初始化
S[u] = 1; //起始点进集合
for(int i=0; i<G.vexnum; i++){ //dist与path数组初始化
dist[i] = G.Edge[u][i]; //表示源点u到顶点i的最短距离
if(u!=i && dist[i]<maxweight) //源点u到i有路径
path[i] = u; //i的前驱结点为u
else
path[i] = -1; //否则i没有前驱结点
}
for(int i=0; i<G.vexnum; i++){ //对所有的顶点处理一次
if(i != u){ //排除源点
int min = maxweight; //选不属于S且具有最短路径的顶点v
int v = u;
for(int j=0; j<G.vexnum; j++){
if(!S[j] && dist[j]<min){ //找最短路径,就是找dist[i]的最小值
v = j; //取最小值的顶点下标
min = dist[j]; //最小值
}
}
S[v] = 1; //将顶点v加入集合
for(int j=0; j<G.vexnum; j++){
//如果源点到顶点j的距离 > 源点经过顶点v再到达j的距离
if(!S[j] && dist[j] > dist[v]+G.Edge[v][j]){
dist[j] = G.Edge[v][j] + dist[v]; //更新最小距离和前驱结点
path[j] = v; //j的前驱结点为v
}
}
}
}
}
输出最短路径及路径长度
void PrintPath(MGraph &G, int u, int dist[], int path[]){
cout<<"各顶点到顶点"<<u<<"的最短路径为:"<<endl;
for(int i=0; i<G.vexnum; i++){
int j=i;
while(j!=u){
cout<<G.Vertex[j]<<" ";
if(j!=u) j=path[j];
}
cout<<G.Vertex[j]<<" dist="<<dist[i]<<endl;
}
}
对于下图,运行结果为:
Floyd算法用于求所有顶点之间的最短路径。
求所有顶点之间的最短路径问题的提法是:已知一个带权有向图,对每一对顶点 v i ≠ v j v_i≠v_j vi=vj,要求求出 v i v_i vi与 v j v_j vj之间的最短路径和最短路径长度。
Floyd算法的基本思想:
设置一个 n × n n×n n×n的方阵 A ( k ) A^{(k)} A(k),其中除对角线的元素都等于0外,其他元素 a ( k ) [ i ] [ j ] ( i ≠ j ) a^{(k)}[i][j](i≠j) a(k)[i][j](i=j)表示从顶点 v i v_i vi到顶点 v j v_j vj的路径长度, k k k表示绕行第 k k k个顶点的运算步骤。
算法步骤:
<1>初始时,对于任意两个顶点 v i v_i vi和 v j v_j vj,若它们之间存在边,则以此边上的权值作为它们之间的最短路径长度;若它们之间不存在有向边,则以maxweight(机器可表示的在问题中不会遇到的最大数,表示∞)作为它们之间的最短路径长度。
<2>以后逐步尝试在原路径上加入顶点 k ( k = 0 , 1 , … … , n − 1 ) k(k=0,1,……,n-1) k(k=0,1,……,n−1)作为中间顶点。如果增加中间顶点后得到的路径比原来的路径长度减少了,则以此新路径代替原路径。
代码实现:
void Floyd(MGraph &G, int A[][maxvertexnum], int P[][maxvertexnum]){
//A[i][j]是顶点i和顶点j之间的最短路径长度
//初始化
for(int i=0; i<G.vexnum; i++){
for(int j=0; j<G.vexnum; j++){
A[i][j] = G.Edge[i][j]; //A矩阵初始时其实是邻接矩阵
P[i][j] = j; //记录路径的数组,记录对应点的最小路径的前驱点
}
}
//对每一个k,产生A(k)
for(int k=0; k<G.vexnum; k++){
for(int i=0; i<G.vexnum; i++){
if(i != k){
for(int j=0; j<G.vexnum; j++){
if(j!=k && A[i][k]+A[k][j] < A[i][j]){
A[i][j] = A[i][k]+A[k][j];
P[i][j] = P[i][k];
}
}
}
}
}
}
输出最短路径及路径长度:
void PrintPath(MGraph &G, int A[][maxvertexnum], int P[][maxvertexnum]){
for(int i=0; i<G.vexnum; i++){
for(int j=0; j<G.vexnum; j++){
if(i!=j){
cout<<i<<"->"<<j<<"的路径:";
int k = P[i][j];
cout<<i<<"->";
while(k!=j){
cout<<k<<"->";
k = P[k][j];
}
cout<<j<<setw(10)<<",dist = "<<A[i][j]<<endl;
cout<<endl;
}
}
}
}
输入及运行结果:
关于Dijkstra算法与Floyd算法的完整运行代码,由于文章篇幅原因不再给出,可到我的资源中免费下载:最短路径的Dijkstra算法及Floyd算法 。