数据结构+图的基本应用

一、问题描述

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

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

相关推荐
ideaout技术团队1 小时前
leetcode学习笔记2:多数元素(摩尔投票算法)
学习·算法·leetcode
代码充电宝1 小时前
LeetCode 算法题【简单】283. 移动零
java·算法·leetcode·职场和发展
不枯石4 小时前
Matlab通过GUI实现点云的均值滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab·均值算法
不枯石4 小时前
Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
白水先森6 小时前
C语言作用域与数组详解
java·数据结构·算法
想唱rap6 小时前
直接选择排序、堆排序、冒泡排序
c语言·数据结构·笔记·算法·新浪微博
老葱头蒸鸡7 小时前
(27)APS.NET Core8.0 堆栈原理通俗理解
算法
视睿7 小时前
【C++练习】06.输出100以内的所有素数
开发语言·c++·算法·机器人·无人机
柠檬07118 小时前
matlab cell 数据转换及记录
算法
YuTaoShao9 小时前
【LeetCode 每日一题】2221. 数组的三角和
数据结构·算法·leetcode