1. 是否有简单路径
通过前面图的基本术语学习我们知道,简单路径是除了开始点和结束点可以相同外,其余顶点均不相同的路径。
图1-是否有简单路径
如图1所示,现在有这样的一个问题:假设图G采用邻接表存储,设计一个算法,判断顶点u到v是否有简单路径?
思路:简单路径正好跟图遍历中所说过的,在遍历过程中访问的顶点是不能重复的,因此我们可以根据深度优先搜索算法的策略,从顶点u开始遍历,当搜索到顶点v时,表明从顶点u到顶点v有路径,且一定为简单路径。
算法实现:
int visited[MAXV]; //全局数组,调用前置为全0
void ExistPath(ALGraph *G,int u,int v, bool &has)
{
int w;
ArcNode *p;
visited[u]=1;
//当u == v时则说明有简单路径,把has更改为true
if(u==v)
{
has=true;
return;
}
//进行深度优先搜索
p=G->adjlist[u].firstarc;
while (p!=NULL)
{
w=p->adjvex;
if (visited[w]==0)
ExistPath(G,w,v,has);
p=p->nextarc;
}
}
void HasPath(ALGraph *G,int u,int v)
{
int i;
//初始化flag标记和visited数组
bool flag = false;
for (i=0; i<G->n; i++)
visited[i]=0;
//进行深度优先搜索
ExistPath(G,u,v,flag);
if(flag)
printf("有\n");
else
printf("无\n");
}
“是否有简单路径”的算法执行过程,如下图所示:
图2- “是否有简单路径”的算法执行过程
2. 输出简单路径
问题:
假设图G采用邻接表存储,设计一个算法输出图G中从顶点u到v的一条简单路径(假设图G中从顶点u到v至少有一条简单路径)。
思路:
1. 还是采用深度优先遍历的方法,在深度优先遍历的过程中记录下遍历到的简单路径
2. 于是在深度优先遍历算法的基础上增加形参
path:存放顶点u到顶点v的路径
d:表示path中存放的路径长度,初值为-1
3. 当从顶点u遍历到顶点v后(也就是当u == v),输出path并返回
算法实现:
int visited[MAXV];
void FindAPath(ALGraph *G,int u,int v,int path[],int d)
{
int w,i;
ArcNode *p;
visited[u]=1;
//path用于记录遍历到的简单路径
d++;
path[d]=u;
if (u==v)
{
//找到一条路径后输出并返回
printf("一条简单路径为:");
for (i=0; i<=d; i++)
printf("%d ",path[i]);
printf("\n");
return;
}
//进行深度优先遍历
p=G->adjlist[u].firstarc;
while (p!=NULL)
{
w=p->adjvex;
if (visited[w]==0)
FindAPath(G,w,v,path,d);
p=p->nextarc;
}
}
void APath(ALGraph *G,int u,int v)
{
int i;
int path[MAXV];
for (i=0; i<G->n; i++)
visited[i]=0;
FindAPath(G,u,v,path,-1);
}
如果你理解了“是否有简单路径”的算法执行过程,那么再理解“输出简单路径”的算法执行过程就不难了。
3. 输出所有简单路径
问题:输出从顶点u到v的所有简单路径。
图3 - 输出所有简单路径
思路:
1. 还是采用深度优先遍历的方法,从顶点u开始进行深度优先搜索,在搜索过程中记录下遍历到的简单路径
2. 于是在深度优先遍历算法的基础上增加形参
path:保存搜索过的路径,把当前的搜索线路记录下来
d:记录走过的路径长度
3. 若当前搜索到的顶点u等于v时,表示找到了一条路径,则输出路径path
4. 当处理完一个顶点后,将其标记为未访问,以寻找下一种可能(这一步操作我们叫做回溯
——这一条路径不算,继续找下一跳路径)
算法实现:
int visited[MAXV];
void FindPaths(ALGraph *G,int u,int v,int path[],int d)
{
int w,i;
ArcNode *p;
visited[u]=1;
d++;
path[d]=u;
//是否找到另一种可能的简单路径
if (u==v && d>1)
{
//则输出找到的路径
printf(" ");
for (i=0; i<=d; i++)
printf("%d ",path[i]);
printf("\n");
}
p=G->adjlist[u].firstarc;
while(p!=NULL)
{
w=p->adjvex;
if (visited[w]==0)
FindPaths(G,w,v,path,d);
p=p->nextarc;
}
//这一步非常重要,体现回溯(回到上一次递归调用时)
visited[u]=0;
}
如果你有仔细看的话就会发现,其实这个算法和前面讲的2个算法大部分都是相同的,而唯一比较难理解的就是递归后的回溯了,这也是最重要的一步
,而理解这一步最好的方式最好是通过调试的方式跟着代码逻辑过一遍。
4. 输出某些简单路径
问题:输出图G中从顶点u到v的长度为s的所有简单路径。
思路:其实这个算法的思路和输出所有路径的算法是一样的,只需在输出时限制长度
算法实现:
int visited[MAXV];
//参数s用于指定简单路径长度
void SomePaths(ALGraph *G,int u,int v,int s,int path[],int d)
{
int w,i;
ArcNode *p;
visited[u]=1;
d++;
path[d]=u;
//当d == s说明找到了长度为s的简单路径
if (u==v && d==s)
{
//输出找到的路径
printf(" ");
for (i=0; i<=d; i++)
printf("%d ",path[i]);
printf("\n");
}
p=G->adjlist[u].firstarc;
while(p!=NULL)
{
w=p->adjvex;
if (visited[w]==0)
SomePaths(G,w,v,s,path,d);
p=p->nextarc;
}
//体现回溯
visited[u]=0;
}