数据结构+图的基本应用

一、问题描述

求有向图的简单路径 编写一个程序,设计相关算法完成以下功能。

(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 时,在队列中找出对应的路径。由广度优先搜 索可知,找到的路径一定是最短路径。

相关推荐
小林熬夜学编程13 分钟前
C++第五十一弹---IO流实战:高效文件读写与格式化输出
c语言·开发语言·c++·算法
蠢蠢的打码18 分钟前
8584 循环队列的基本操作
数据结构·c++·算法·链表·图论
无问8172 小时前
数据结构-排序(冒泡,选择,插入,希尔,快排,归并,堆排)
java·数据结构·排序算法
Lenyiin2 小时前
《 C++ 修炼全景指南:十 》自平衡的艺术:深入了解 AVL 树的核心原理与实现
数据结构·c++·stl
程序猿进阶3 小时前
如何在 Visual Studio Code 中反编译具有正确行号的 Java 类?
java·ide·vscode·算法·面试·职场和发展·架构
Eloudy3 小时前
一个编写最快,运行很慢的 cuda gemm kernel, 占位 kernel
算法
slandarer3 小时前
MATLAB | R2024b更新了哪些好玩的东西?
java·数据结构·matlab
king_machine design3 小时前
matlab中如何进行强制类型转换
数据结构·算法·matlab
西北大程序猿3 小时前
C++ (进阶) ─── 多态
算法
无名之逆3 小时前
云原生(Cloud Native)
开发语言·c++·算法·云原生·面试·职场和发展·大学期末