一、问题描述
求有向图的简单路径 编写一个程序,设计相关算法完成以下功能。
(1)输出如图所示的有向图 G 从顶点 5 到顶点 2 的所有简单路径。
(2)输出如图所示的有向图 G 从顶点 5 到顶点 2 的所有长度为 3 的简单路径。
(3)输出如图所示的有向图 G 从顶点 5 到顶点 2 的所有最短路径。
二、问题解决
cpp
#include <stdio.h>
#include <malloc.h> #define INF 32967 //定义∞
#define MAXV 100 //最大顶点个数
typedef char InfoType;
typedef struct
{
int no; //顶点编号
InfoType info; //顶点信息
}VertexType; //顶点类型
typedef struct ArcNode
{
int adjvex; //该边的邻接点编号
struct ArcNode *nextarc; //指向下一条边的指针
int weight; //该边的相关信息,如权值(用整型
表示)
}ArcNode; //边结点类型
typedef struct VNode
{
InfoType info; //顶点其他信息
int cnt; //存放顶点入度,仅用于拓扑排序
ArcNode *firstarc; //指向第一条边
}VNode; //邻接表结点类型
typedef struct
{
VNode adjlist[MAXV]; //邻接表头结点数组
int n; //图中顶点数
int e; //图中边数
}AdjGraph; //完整的图邻接表类型
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e)
{
int i, j;
ArcNode *p;
G = (AdjGraph *)malloc(sizeof(AdjGraph));
for(i = 0; i < n; i++) //给邻接表
中所有头结点的指针域置初值 NULL {
G->adjlist[i].firstarc = NULL;
}
for(i = 0; i < n; i++) //检查邻接
矩阵中的每个元素
{
for(j = n - 1; j >= 0; j--)
{
if(A[i][j] != 0 && A[i][j] != INF) //存在一条
边
{
p = (ArcNode *)malloc(sizeof(ArcNode)); //创建一个结
点 p
p->adjvex = j; //邻接点编
号
p->weight = A[i][j]; //边的权重
p->nextarc = G->adjlist[i].firstarc; //采用头插
法插入结点 p
G->adjlist[i].firstarc = p;
}
}
}
G->n = n;
G->e = e;
}
void DestroyAdj(AdjGraph *&G)
{
ArcNode *pre, *p;
for(int i = 0; i < G->n; i++)
{
pre = G->adjlist[i].firstarc; //pre 指向第
i 个单链表的首结点
if(pre != NULL)
{
p = pre->nextarc;
while(p != NULL) //释放第 i
个单链表的所有边结点 {
free(pre);
pre = p;
p = p->nextarc;
}
free(pre);
}
}
free(G); //释放头结点数组
}
//深度优先遍历
int visited[MAXV]; //全局数组
void allPath(AdjGraph *G, int u, int v, int path[], int d)
{
ArcNode *p;
int i;
int w;
d++; //路径长度增 1
path[d] = u; //当前顶点添加到路径中
visited[u] = 1; //置已访问标记
if(u == v && d > 0) //找到终点
{
for(i = 0; i <= d; i++)
printf("%3d", path[i]);
printf("\n");
}
p = G->adjlist[u].firstarc; //p 指向顶点 u 的第一个相邻点
while(p != NULL)
{
w = p->adjvex; //w 为 u 的相邻点编号
if(visited[w] == 0) //若该顶点未标记访问,则递归访问之
allPath(G, w, v, path, d);
p = p->nextarc; //找 u 的下一个相邻点
}
visited[u] = 0; //取消访问标记,以使该顶点可重新使
用
}
void allPath_len2(AdjGraph *G, int u, int v, int len, int path[],
int d)
{
int i;
int w;
ArcNode *p; //边结点类型指针
visited[u] = 1; //置已访问标记
d++; //路径长度增 1
path[d] = u; //将当前顶点添加到路径中
if(u == v && d == len) //满足条件,输出一条路径
{
for(i = 0; i <= d; i++)
printf("%3d", path[i]);
printf("\n");
}
p = G->adjlist[u].firstarc; //p 指向顶点 u 的第一个相邻点
while(p != NULL)
{
w = p->adjvex; //w 为顶点 u 的相邻点编号
if(visited[w] == 0) //若该顶点未标记访问,则递归访问之
allPath_len2(G, w, v, len, path, d);
p = p->nextarc; //找 u 的下一个相邻点
}
visited[u] = 0; //取消访问标记,以使该顶点可重新使
用
}
//广度优先遍历
int short_path(AdjGraph *G, int u, int v, int path[])
{
struct
{
int vno; //当前顶点编号
int level; //当前顶点的层次
int parent; //当前顶点的双亲结点在队列中的下标
}qu[MAXV]; //定义顺序非循环队列
int qu_front = -1, qu_rear = -1, k, lev, i, j;
ArcNode *p;
visited[u] = 1;
qu_rear++; //顶点 u 已访问,将其入队 qu[qu_rear].vno = u;
qu[qu_rear].level = 0;
qu[qu_rear].parent = -1;
while(qu_front < qu_rear) //队非空循环
{
qu_front++;
k = qu[qu_front].vno; //出队顶点 k
lev = qu[qu_front].level;
if(k == v) //若顶点 k 为终点
{
i = 0;
j = qu_front;
while(j != -1)
{
path[lev - i] = qu[j].vno;
j = qu[j].parent;
i++;
}
return lev; //找到顶点 v,返回其层次
}
p = G->adjlist[k].firstarc;//p 指向顶点 k 的第一个相邻点
while(p != NULL) //依次搜索 k 的相邻点
{
if(visited[p->adjvex] == 0)//若未访问过
{
visited[p->adjvex] = 1;
qu_rear++;
qu[qu_rear].vno = p->adjvex;//访问过的相邻点进队
qu[qu_rear].level = lev + 1;
qu[qu_rear].parent = qu_front;
}
p = p->nextarc; //找到顶点 k 的下一个相
邻点
}
}
return -1;
}
int main()
{
int i, j;
int u = 5, v = 2, len = 3;
int path[MAXV];
AdjGraph *G;
int A[MAXV][MAXV] = {
{0, 1, 0, 1, 0, 0},
{0, 0, 1, 0, 0, 0},
{1, 0, 0, 0, 0, 1},
{0, 0, 1, 0, 0, 1},
{0, 0, 0, 1, 0, 0},
{1, 1, 0, 1, 1, 0}
};
int n = 6; //顶点数
int e = 12; //边数
CreateAdj(G, A, n, e);
printf("(1)从顶点%d 到%d 的所有路径:\n", u, v);
for(i = 0; i < n; i++)
visited[i] = 0;
allPath(G, u, v, path, -1);
printf("(2)从顶点%d 到%d 的所有长度为%d 路径:\n", u, v, len);
allPath_len2(G, u, v, len, path, -1);
printf("(3)从顶点%d 到%d 的最短路径:\n", u, v);
for(i = 0; i < n; i++)
visited[u] = 0;
j = short_path(G, u, v, path);
for(i = 0; i <=j; i++)
printf("%3d", path[i]);
printf("\n");
DestroyAdj(G);
return 0;
}
三、代码分析
1、输出有向图 G 从顶点 5 到顶点 2 的所有简单路径:采用从顶点 u 出发的回 溯深度优先搜索方法,当搜索到顶点 v 时输出路径 path[0..d],然后继续回溯 查找其他路径。
2、输出有向图 G 从顶点 5 到顶点 2 的所有长度为 3 的简单路径:采用从顶点 u 出发的回溯深度优先搜索方法,每搜索一个新顶点,路径长度增 1,若搜索 到顶点 v 且 d 等于 len,则输出路径 path[0..d],然后继续回溯查找其他路 径。
3、输出有向图 G 从顶点 5 到顶点 2 的所有最短路径:采用从顶点 u 出发广度 优先搜索方法,当搜索到顶点 v 时,在队列中找出对应的路径。由广度优先搜 索可知,找到的路径一定是最短路径。