1. 求最短路径
这几篇将主要根据我们之前所学的图的遍历算法来解决一些问题,下面我们来看这样的一个问题。
问题:求不带权连通图G中从顶点u到顶点v的一条最短路径。(即求顶点u到顶点v之间边数最少的顶点序列)。
图1-求最短路径
例如,对于这样的一个有向图,我们要求顶点0到顶点7的最短路径。对于求最短路径,我们是选择DFS还是BFS呢?这是一个值得思考的问题。
2. DFS or BFS
首先来看DFS深度优先搜索:
图2-DFS深度优先搜索
如果采用DFS深度优先搜索的话,得到的路径可能是0 - 1 - 3 - 6 - 7这样的路径,而根据之前我们学习DFS深度优先搜索过程可知,简单来说,DFS深度优先搜索就是选择一个方向(顶点),然后依次访问下去(访问邻接点)
,直到访问到终点为止,在这个过程中DFS深度优先搜索并不关心这是否是一条最短路径
,而从最终搜索到的路径来看,这显然不是一条最短路径。
我们再来看BFS广度优先搜索:
图3-BFS广度优先搜索
从图3来看,BFS广度优先搜索是对每一层的所有相邻的顶点进行搜索,直到找到终点为止。在这个搜索过程中,广度优先搜索会对所有的邻接点进行了访问
,因此当广度优先搜索完成后,我们就可以知道有这样的一条最短路径可以到达顶点v(即序列:0 - 2 - 5 - 7)。
3. 广度优先搜索算法思路
1. 从顶点u出发一层一层地向外扩展
2. 利用队列记录访问的顺序
3. 当第一次找到顶点v时队列中便包含了最短路径,同时在搜索过程中利用队列存储最短路径(这是一个普通队列,而非之前所说的环形队列)
4. 然后再利用队列输出最短路径(逆路径)
如果你还是有些疑惑的话(比如逆路径),不要急,接下来我们还会继续介绍。
4. 求最短逆路径算法
最好是先把BFS算法先熟悉一下,有助于理解这个求最短逆路径算法,求最短逆路径算法的实现:
//求最短逆路径算法
void ShortPath(ALGraph *G,int u,int v)
{
ArcNode *p;
int w,i;
struct QUERE qu[MAXV];
//int front;
//int rear;
//设置visited数组
int visited[MAXV];
for(i=0; i<G->n; i++)
visited[i]=0;
//对起始点标记为已访问
visited[u]=1;
//设置队列
int front = -1;
int rear = -1;
rear++;
//将起始点入队,前驱设置为-1
qu[rear].data=u;
qu[rear].parent=-1;
while (front!=rear) //队不空循环
{
//出队顶点w
front++;
w=qu[front].data;
//然后判断是否为要找的终点
//知道出队顶点w为7的时候,就可以输出路径了
if (w==v)
{
//找到v时输出路径之逆并退出
/*
注意:因为这条路径是逆路径,所以要从顶点7开始输出,
每次输出完后,开始找parent,根据parent找到上一个顶点的下标位置并输出
,以此类推,直到找到parent为-1则说明逆路径输出完毕
*/
i=front;
while (qu[i].parent!=-1)
{
printf("%2d ",qu[i].data);
i=qu[i].parent;
}
printf("%2d\n",qu[i].data);
break;
}
//访问w的所有邻接点(这一步操作非常重要,体现BFS广度优先搜索,相信大家对这一步操作已经不陌生了吧)
p=G->adjlist[w].firstarc;
while (p!=NULL)
{
if (visited[p->adjvex]==0)
{
visited[p->adjvex]=1;
rear++;
qu[rear].data=p->adjvex;
qu[rear].parent=front;
}
p=p->nextarc;
}
}
}
5. 求最短路径算法的过程
图4-求最短路径算法的过程
还记得前面我们说过的逆路径
吗?求最短逆路径算法过程如图4所示,BFS广度优先搜索会将所有搜索到的顶点进行入队,每个顶点入队时会记录两项信息,其中data域记录顶点的信息,parent域记录上一个顶点的下标位置。比如对于顶点5来说,data会记录顶点5的值,而parent域会记录顶点5的上一个顶点2的下标位置(下标为2)。这样我们就可以通过这种方式输出逆路径了。
而求最短逆路径算法中的顶点w则会记录每个出队的顶点,所以当顶点w为顶点7时,此时顶点w和顶点v相等,则说明找到了最短逆路径,所以会从顶点7开始输出,并根据顶点7的parent域找到上一个顶点,并输出顶点5,以此类推,直到顶点的parent为-1,这说明逆路径就输出完毕了。