单源无权最短路问题所要掌握的点:
·求出图中源点到达图中任意顶点的所有路径中最短的路径的长度
·求出图中源点到达图中任意顶点的所有路径中最短的路径的走法
由于图无权(或者称权为1),所以离源点的距离越大,路径长度越长,所以解决单源无权最短路问题只需求出源点到任意某点的所有路径中所要经过其它中间点的数量最少的路径。
(源点为点1。到点3有两条路径1→2→3 和 1→3,第1条路径经过1个中间点,第2条路径不经过中间点,故点1到点3的最短路径为1→3)
我们可以先确定中间点有0个的点(路径长度为1的点),这些点路径长度为1的作为1个中间点,再去确定中间点有1个的点(路径长度为2的点),再将这些路径长度为2的点去确定中间点有2个的点(路径长度为3的点),循环往复,最终确定路径长度最长的点。
(源点为点1。先确定2、3两点路径为1,再通过点3确定点4路径为2)
这种路径的寻找方式可使用在树的遍历与图的遍历中所学到广度优先搜索BFS(如果没学过建议先去打基础,不要跳着学)。
(层序遍历代码与单源无权最短路代码比较)
我们需要定义dist数组储存路径长度,path数组储存其路径上的前一个点,并且让path数组兼具visited数组的判断顶点是否已经被游览过的功能。若要输出源点到某点的路径,可用栈压入后弹出输出或者用递归算法。
#include<stdio.h>
typedef int ElementType;//int中可改为所需要的数据类型
int v[100][100],path[100],dist[100];/*v[100][100]为邻接矩阵,path[100]储存其路径上的前一个点,
dist[100]储存路径长度,并且判断顶点是否已被游览*/
ElementType vdata[100];//有需要可添加顶点数据,这里简化问题,不输入顶点数据
int queue[100], head = 0, tail = 0;//层序遍历的队列
int main()
{
int vnum, arcnum;//vnum为顶点数,arcnum为边数
scanf_s("%d %d", &vnum, &arcnum);
for (int i = 1; i <= vnum; i++)//由于dist数组兼具判断顶点是否已被游览的功能,所以要将其初始化为一个不可能的定值
dist[i] = -1;
int v1, v2;
/*储存边的两个顶点,若输入的是顶点数据data,则需另设一个search函数找到顶点的序号。
这里简化问题,直接给顶点序号*/
for (int i = 1; i <= arcnum; i++)//输入边
{
scanf_s("%d %d", &v1, &v2);
v[v1][v2] = 1;
v[v2][v1] = 1;
}
queue[tail++] = 1; dist[1] = 0; path[1] = 1;//源点进队列,路径长度初始化为0,路径上前一顶点初始化为该源点的位置
int vpop;//储存出队列的顶点
while (head != tail)//判断队列是否为空,以判断所有顶点是否全部被游览过
{
vpop = queue[head++];//出队列
for (int i = 1; i <= vnum; i++)
{
if (v[vpop][i] && dist[i] == -1)//存在边且未被读过
{
queue[tail++] = i;//进队列
dist[i] = dist[vpop] + 1;//该顶点路径长度为前一顶点路径长度+1
path[i] = vpop;//该顶点前一顶点为其路径前一点
}
}
}
//以下代码为测试代码
for (int i = 1; i <= vnum; i++)//输出队列长度
printf("%d ",dist[i]);
printf("\n");
for (int i = 1; i <= vnum; i++)//输出顶点路径上的前一顶点
printf("%d ", path[i]);
}
推荐免费课程:浙江大学-数据结构-无权图的单源最短路径(看完需要约10分钟,可倍数播放)