其实求最短路径的算法有很多种,现在我先解析下第一种:dijkstra算法。用这个算法来求最短路径的前提是每条边都必须为大于等于0的边,如果相求小有于零的边的最短路径,则可选择其他一个非常经典的算法来求,在这里就不多说了,如果你学过图论,那你一定知道是那个算法。无论是有向图还是无向图,他们都适用dijkstra算法适用求解最短路径,这个一定要知道。
其实对于一个新人来说,图论是一个非常难懂得东西,所以入门非常支吃力,当然也不排除一些高手们,这些基本算法了如指掌。在此之前,你可能也会只会求图的最段路径,只有开始节点v0,没有结束节点或者节点为最后一个Vn-1.但是你看过这个算法改进之后,你想求哪两个节点的最短路径就求哪两个。废话少说,其实用dijkstra算法来求最短路径可以看做只有三步:一、取第一个点,即开始那个点;二、取第二个点;三、取第三个点。最后就这么循环,得到两个节点最短路径的数组,这个数组保存个节点的最近前缀。
按照dijkstra算法的基本步骤,如下:
:
我们可以从步骤中看出,其实就是取三点中其中两点到达第三点的最短路径的那个点。现在我们的问题是去想办法怎么找出那三个点来比较而已。所以这样一想就简单多了,就是找三个点而已吧。接下来,我们要找的点是符合一些限制条件的:!s[k] && arc[u][k]<maxm && dist[u] + arc[u][k] < dist[k]。所以接下来还差一部分,怎么去找第二个点和第三个点。而第一个点是定好的了。
1、 找第二个点就是在第一个点的基础上,找出和第一个点有路径的并且取值最小的点,这样一来,我们就可以把这个想成查找最小值得办法了。因此这部就简单多了。
2、接下来怎么着第三个点呢?其实大家都知道,找和第一个点和第二个点都有关系的点(都可达的),所以,这样第三个点也出来了。
3、最后就是判断第一个点直接到第三个点的距离近还是第一个点经过第二个点再到第三个点的距离近。最后取离第三个点最近的前缀加入集合。这样一来就ok了,最后就得到最短路径的数组,这个数组保存个节点的最近前缀。
4、通过终点倒过来查找是否有路径到达第一个点,最后就可以得到所求最短路径了。
算法如下:
//本提认为顶点自己是没有回环路的,即自己不可达,注意:数组path保存的是该节点的前面一个节点,这个很重要,必须注意!!
#include<stdio.h>
#include<stdlib.h>
#define maxm 100000//无限大权值,表示不可达
#define maxn 20//最大定顶点数
int arc[6][6] = {
0, 2, 10, 0, 0,0,
0,0, 0, 0, 8 ,0,
0, 0, 0,10, 0 ,0,
0, 0, 0, 0, 10 ,0,
0, 0, 0, 0,0,10,
0, 0, 0,0, 0,0
};//初始化邻接矩阵
int path[maxn],s[maxn],dist[maxn],pathr[maxn];//遍历路径、标志顶点是否加入、始点到个点的距离、需找路径,本体倒过来存储
void dijkstra(int n,int v,int endv)
{
for (int i = 0; i < n; i++)//将为零的边,即不可达的边权值给最大值,这部其实可设略,不过下面要有所改动,将<maxm改为>0即可
{
for (int j = 0; j < n; j++)
{
if (arc[i][j] == 0) arc[i][j] = maxm;
}
}
for (int i = 0; i < n; i++)//初始化始点到各店的距离,并保存在dist数组中,将标志位s初始化为零,从v到各点的路径可达则给各前缀定为v,若不可达则值为-1
{
dist[i] = arc[v][i];
s[i] = 0;
if (i!=v && dist[i]<maxm)
{
path[i] = v;
}
else
{
path[i] = -1;
}
}
//选取第一个点
s[v] = 1; dist[v] = 0;//首先将v假如路径集合中,开始时距离为0,并将v标志为改变
for (int i = 0; i < n-1; i++)
{
int min = maxm, u = v;
for (int j = 0; j < n; j++)
{
//其实这里是选取第二个点
if (!s[j] && dist[j] < min)//寻找从v到相邻并没加入集合path的节点的最短路径的节点
{
u = j;
min = dist[j];
}
}
s[u] = 1;//将第二个点加入集合
//接着选取第三个点
for (int k = 0; k < n; k++)
{
if (!s[k] && arc[u][k]<maxm && dist[u] + arc[u][k] < dist[k])//取最短的节点路径,将前缀加入集合path
{
dist[k] = dist[u] + arc[u][k];
path[k] = u;
}
}
}
if (path[endv] == -1)//排除自己可达自己
{
printf("该点不可达!");
return;
}
int t = 1;
int e = endv;
pathr[0] = endv;
while (path[endv] != v)//通过邻接表查找最短路径
{
pathr[t++] = path[endv];
endv = path[endv];
}
pathr[t] = v;
for (int i = t; i > 0;i--)//将路径输出,如果输出权值为零,则说明不可达
{
printf("%d->",pathr[i]);
}
printf("%d:%d\n", pathr[0],dist[e]);
}
int main()
{
dijkstra(6,0,6);
system("pause");
return 0;
}