title: 数据结构lab3-图型结构的建立与搜索
date: 2023-05-16 11:42:26
tags: 数据结构与算法
课程名称:数据结构与算法
课程类型:必修
实验项目:图型结构的建立与搜索
实验题目:图的存储结构的建立与搜索
实验日期:2021.11.10
班级:2003001
学号:120L021011
姓名:石卓凡
一、实验目的
1. 掌握图的邻接矩阵、邻接表等不同存储形式的表示方法。
2. 掌握图的两种不同遍历方法的基本思想并能编程实现。
3. 掌握构造最小生成树的两种算法思想,并能编程实现。
4. 掌握求单源最短路径和任意两顶点之间的最短路径的算法。
5. 掌握求关键路径的算法,并能编程实现。
6. 能够灵活运用图的相关算法解决相应的实际问题。
**二、**实验要求及实验环境
1**.分别实现图的邻接矩阵、邻接表存储结构的建立算法,分析和比较各建立算法的时间复杂度以及存储结构的空间占用情况;**
2**.实现图的邻接矩阵、邻接表两种存储结构的相互转换算法;**
3**.在上述两种存储结构上,分别实现图的深度优先搜索(递归和非递归)和广度优先搜索算法。并以适当的方式存储和显示相应的搜索结果(深度优先或广度优先生成森林(或生成树)、深度优先或广度优先序列和编号);**
4**.**分析搜索算法的时间复杂度;
5**.以文件形式输入图的顶点和边,并显示相应的结果。要求顶点不少于10个,边不少于13个;**
6**.软件功能结构安排合理,界面友好,便于使用。**
实验环境:
操作系统:Win7/Win10
集成开发环境:devcpp++
外部库:暂无
三、设计思想(本程序中的用到的所有数据类型的定义,主程序的流程图及各程序模块之间的调用关系)
数据类型定义
typedef struct TNode {
char Data; //结点数据
int Parent; //该结点双亲再数组中的下标
}TNode;
typedef struct Tree{
TNode node[MaxVNode]; //结点数组
int numTNode; //结点数量
}Tree;
Tree T;
typedef struct MatGraph//有向图的邻接矩阵
{
char VNode[MaxVNode];
int ENode[MaxVNode][MaxVNode];
int numENode;
int numVNode;
}MatGraph;
typedef struct ENode
{
int AdjV;//这条边的终点顶点对应的下标
int Weight;
struct ENode *Next;//链域,有着同一个起点的下一条边
}ENode;
typedef struct VNode
{
char Data;
ENode* FirstENode;//由该顶点V出发的第一条边
}VNode,AdjList[MaxVNode];
typedef struct ALGraph//有向图的邻接表
{
AdjList adjlist;
int numVNode;
int numENode;
}ALGraph;//ALGraph
1.本系统总计设计了23个函数,每个函数的功能和接口设计如下表所示:
序号 | 函数名 | 函数功能 | 函数参数 | 函数返回值 |
---|---|---|---|---|
1 | Main | 根据order执行各函数 | 无 | 无 |
2 | InitMatGraph | 初始化Mat | MatGraph &G | NULL |
3 | InsertVNode | 前序遍历 | MatGraph &G,char V | NULL |
4 | InsertENode | 有向图MatG插入边 | MatGraph &G,char V,char W,int Weight | NULL |
5 | CreatMatGraph | 读入边和顶点建立邻接矩阵 | MatGraph &G | NULL |
6 | Mat_DFS | MatGraoh的DFS | MatGraph G,int start | NULL |
7 | Mat_BFS | MatGraoh的BFS | MatGraph G,int start | NULL |
8 | ShowMatGraph | 进行展示邻接矩阵 | MatGraph G | NULL |
9 | ShowALGraph | 进行展示邻接表 | ALGraph* G | NULL |
10 | Mat_DFS_Tree | 邻接矩阵DFS用树展示 | MatGraph G,int start | NULL |
11 | Mat_DFS_BianHao | 邻接矩阵DFS用编号展示 | MatGraph G,int start,int count,int arr[]) | NULL |
12 | Mat_BFS_Tree | 邻接矩阵BFS用树展示 | MatGraph G,int start | NULL |
13 | Mat_BFS_BianHao | 邻接矩阵DFS用编号展示 | MatGraph G,int start,int count,int arr[] | NULL |
14 | AL_DFS_Tree | 邻接表DFS用树展示 | ALGraph* G,int v | NULL |
15 | AL_DFS_BianHao | 邻接表DFS用编号展示 | ALGraph* G,int v,int count,int arr[] | NULL |
16 | AL_BFS_Tree | 邻接表BFS用树展示 | ALGraph* G,int v | NULL |
17 | AL_BFS_BianHao | 邻接表BFS用编号展示 | ALGraph* G,int v,int count,int arr[] | NULL |
18 | InitialTree | 初始化树 | NULL | NULL |
19 | ShowTree | 展示树 | (MatGraph &G) | NULL |
20 | Mat_DFS_FeiDiGui | 邻接矩阵DFS非递归 | MatGraph MatG,int start | |
21 | AL_DFS_FeiDiGui | 邻接表DFS非递归 | ALGraph* ALG,int start | |
22 | ListToMat | 已知邻接表G ,转为邻接矩阵MatG | MatGraph &MatG,ALGraph* &ALG | |
23 | MatToList | 已知MatGraph邻接矩阵 有向图 转为 邻接表 | MatGraph MatG,ALGraph* &ALG |
2.逻辑设计
(1)邻接矩阵的建立
1.打开文件,并且初始化InitMatGraph(G);
2.读入顶点个数
2.1 进行循环对应次数读入顶点V,用InsertVNode(G,V);插入顶点
3.读入边个数
3.1 进行循环对应次数读入边V1-V2,权重,利用InsertENode(G,V1,V2,weight);插入边
\4. ShowMatGraph(G)进行展示邻接矩阵
(2)邻接表的建立
1.打开文件
2.读入顶点个数,并且读入边个数
3.for循环依次读入G->adjlist[i].Data,并且将G->adjlist[i].FirstENode=NULL;
4.for循环依次读入边V1-V2以及权重Weight
4.1 新生成一个ENode* E赋值V1,V2,Weight,利用头插法插入邻接表
5.ShowALGraph(G)进行展示邻接表
(3)邻接矩阵的DFS递归
1.访问头元素并且标记为已访问
2.遍历所有的和源元素相关的元素,如果为没有被访问,递归的搜索该顶点;
(4)邻接矩阵的DFS非递归
1.初始化栈
2.将头元素进栈,并且cout,记录已访问
3.循环直到栈为空
3.1 读取栈顶元素
3.2遍历所有能和当前栈顶元素有关的顶点 , 对当前没有访问过的且有边连接的顶点访问和进栈
3.3对与已经遍历完所有能和当前栈顶元素有关的顶点之后,进行出栈
(5)邻接矩阵的BFS
\1. 初始化队列
\2. 对头元素进行进队并且cout和标记已访问
\3. 循环直到队列为空
3.1 v=队列头元素,并且出队
3.2 For循环遍历所有与v相关的顶点,找到有边相连接且没有访问过的顶点进行访问,压栈,并且标记已访问
(6)邻接表的DFS递归
\1. 访问头元素,并且标记为已访问
\2. 遍历所有的和头元素相关的元素,如果为被访问,递归的搜索该顶点;
(7)邻接表的DFS非递归
\1. 初始化栈
\2. 对于头元素进行访问,并且标记已访问,新建立边E,赋值为头元素的FirstENode
\3. While循环直到E为空且栈为空
3.1while循环直到E为空
3.1.1不断对尚未访问的并且可以访问到更深的顶点进行访问
3.1.2对于已经访问过的顶点时候,进行E转为同一个起点顶点的另一条边
3.2当由最初的结点的所有路都遍历完之后,出栈,E等于栈顶元素
(8)邻接表的BFS
\1. 初始化队列
\2. 将头元素结点v进行入队Q,并且访问,且设置为已经访问
\3. While直到Q为空
3.1 temp=队列头元素,进行出队,E等于G->adjlist[temp].FirstENode
3.2 While直到E为空
3.2.1如果边E指向的终点顶点E->AdjV并没有访问过,如果可以访问则进行访问并且对于合适的进入入队
3.2.2 E=E->Next;E更新为同一个出发顶点的下一条边
(9)已知有向图邻接表转为邻接矩阵
\1. 初始化邻接矩阵
\2. 遍历邻接表的每一个顶点,顶点存入邻接矩阵
2.1 E = ALG->adjlist[i].FirstENode;
2.2 while循环将E及其E所有的同一个起点顶点的边都存入邻接矩阵
(10) 有向图邻接矩阵转为邻接表
\1. 初始化邻接表
\2. 遍历邻接矩阵的每一个顶点
2.1 存储顶点进入邻接表
2.2 再次遍历所有顶点,去寻找有无对应的边,并且存储这个顶点所有相关的边进入邻接表
(11) 生成树展示
1.同DFS和BFS遍历框架和大体流程一样
2.但是把访问结点cout改为记录当前Data保存到T.node[i].Data,并且记录对应父节点下标保存至T.node[i].Parent,同时T.numTNode++;
(12) 编号展示
\1. 同DFS和BFS遍历框架和大体流程一样
\2. 多设置了int count记录当前第几个访问,与此同时传入数组int arr[],arr[i]=x代表了下标为i的对应的结点出现的编号是x
3.物理设计
1.生成树Tree用到双亲表示法的顺序存储,Tree内包含了TNode node[MaxVNode]的节点数组,用于记录节点个数的int numTNode;而每一个TNode里面有char型的Data记录值,有int Parent记录当前结点的父节点的下标
2.有向图邻接矩阵MatGraph,有char型的VNode[MaxVNode]顶点结点数组,int型ENode[MaxVNode][MaxVNode]边结点数组;int型numENode记录有多少个边;int型numVNode记录有多少个顶点;
3.有向图邻接表ALGraph,有数据类型为VNode型的数组adjlist;
记录有多少个顶点个数的int型的numVNode;
记录有多少个边个数int型numENode;
而VNode内部,定义了Char型的Data,记录值。定义了ENode* FirstENode,作为该顶点V出发的第一条边
而ENode内部,定义了int型AdjV,记录了这一条边指向的终点顶点,int Weight记录这条边的权值,struct ENode *Next记指向与这一条边有着相同起点顶点的下一条边的地址
4.stack栈用链式存储,栈有指向头节点的指针Node *head指向最初进入的元素(栈底),指向尾结点的Node *p,p指向栈顶最新进入的元素,记录栈元素数量的int length,而每个Node节点有对应数据类型的data,指向下一个结点的Node *next,
5.queue队列用链式存储,有指向队首的指针Node *head,指向队尾的指针Node *rear,记录队列元素多少的int length,而每一个Node结点内部都有,对应数据类型的data,指向下一个结点的Node *next
4主程序流程图及各程序模块之间的调用关系
流程图
调用关系
5.时空复杂度比较分析
(1)有向图邻接矩阵和邻接表存储结构建立算法:
邻接矩阵 | 邻接表 | |
---|---|---|
时间复杂度 | O(n²) | O(n+e) |
空间复杂度 | O(n²) | O(n+e) |
适用范围 | 稠密图 | 稀疏图 |
(2)搜索算法
邻接矩阵 | 邻接表 | |||
---|---|---|---|---|
深度 | 广度 | 深度 | 广度 | |
时间复杂度 | ![]() | ![]() | ![]() | ![]() |
**四、**测试结果
测试样例1:
测试样例2:
五、经验体会与不足
1.经验体会
1.在图的DFS类似于二叉树的先序遍历算法,因此在非递归时候都需要利用到栈来实现
2.非递归算法就是将递归算法用循环和借助栈或者队列来进行操作实现,递归算法更加简洁易懂,非递归算法有助于进一步理解
3.树可以说是一种特殊的图,树是有向无环的特殊的图
4.对于图的邻接矩阵和邻接表,在作为函数传参时候会根据自己的定义有所区别,比如邻接表传入指针型,传入参数时候(MatGraph &G)可以根据自己的需要选择是否要加入&
2.过程暴露的不足:
1.在实现广搜和深搜的生成树时候,如何利用现有的DFS,BFS序列表示修改没有思路
解决方法:将树的存储模式改为双亲表示法的顺序存储
2.如何利用现有的DFS和BFS序列修改为编号表示
解决方法:新建立数组arr[]表示第几次访问到的,同时插入count计数
六、附录:源代码(带注释)
\
#include<iostream>\#include<stdlib.h>\#include<stdio.h>\#include<cstring>\#include<assert.h>using namespace std;\#define INF -1\#define MaxVNode 100int visited[MaxVNode];typedef struct TNode { char Data; //结点数据 int Parent; //该结点双亲再数组中的下标}TNode;typedef struct Tree{ TNode node[MaxVNode]; //结点数组 int numTNode; //结点数量}Tree;Tree T;typedef struct MatGraph//先按照有向图做 { char VNode[MaxVNode]; int ENode[MaxVNode][MaxVNode]; int numENode; int numVNode;}MatGraph;typedef struct ENode{ int AdjV;//这条边的终点顶点对应的下标 int Weight; struct ENode *Next;//链域,有着同一个起点的下一条边 }ENode;typedef struct VNode{ char Data; ENode* FirstENode;//由该顶点V出发的第一条边}VNode,AdjList[MaxVNode];typedef struct ALGraph{ AdjList adjlist; int numVNode; int numENode;}ALGraph;//ALGraphtemplate<class T>class stack{private: struct Node { T data; Node *next; }; Node *head; Node *p; int length;public: stack() { head = NULL; length = 0; } void push(T n)//入栈 { Node *q = new Node; q->data = n; if (head == NULL) { q->next = head; head = q; p = q; } else { q->next = p; p = q; } length++; } void pop()//出栈 不会!!并且不会!!!将出栈的元素返回 { if (length <= 0) { abort(); } Node *q; T data; q = p; data = p->data; p = p->next; delete(q); length--; } int size()//返回元素个数 { return length; } T top()//返回栈顶元素 { return p->data; } bool empty()//判断栈是不是空的 { if (length == 0) { return true; } else { return false; } }};template<class T>class queue{private: struct Node { T data; Node *next; }; Node *head;//! Node *rear;//!队尾 int length;public: queue() { head = NULL; rear = NULL;//!初始化 length = 0; } void push(T n)//入队 { Node *node = new Node; node->data = n; node->next = NULL;//! if (head == NULL) { head = node; rear = node; } else { rear->next = node; rear = node; } length++; } void pop()//出栈 不会!!并且不会!!!将出栈的元素返回 { if (length <= 0) { abort(); } Node *temp = head; head = head->next; delete (temp); length--; } int size()//返回元素个数 { return length; } T front()//!返回队首元素 { return head->data; } bool empty()//判断栈是不是空的 { if (length == 0) { return true; } else { return false; } }};void InitMatGraph(MatGraph &G);//初始化Mat void InsertVNode(MatGraph &G,char V);//MatG插入V void InsertENode(MatGraph &G,char V,char W,int Weight);//向图MatG插入边 void CreatMatGraph(MatGraph &G); //读入边和顶点建立邻接矩阵void Mat_DFS(MatGraph G,int start);//MatGraoh的DFS void Mat_BFS(MatGraph G,int start);//MatGraoh的BFS void ShowMatGraph(MatGraph G);//进行展示邻接矩阵 void ShowALGraph(ALGraph* G);//进行展示邻接表 void Mat_DFS_Tree(MatGraph G,int start);//MatGraoh的DFS 树展示 void Mat_DFS_BianHao(MatGraph G,int start,int count,int arr[]);//MatGraoh的DFS void Mat_BFS_Tree(MatGraph G,int start);void Mat_BFS_BianHao(MatGraph G,int start,int count,int arr[]);void AL_DFS_Tree(ALGraph* G,int v);void AL_DFS_BianHao(ALGraph* G,int v,int count,int arr[]);void AL_BFS_Tree(ALGraph *G,int v);void AL_BFS_BianHao(ALGraph *G,int v,int count,int arr[]);void InitialTree();//初始化树 void InitMatGraph(MatGraph &G)//初始化Mat { memset(G.VNode,'#',sizeof(G.VNode));//全部初始化# memset(G.ENode,INF,sizeof(G.ENode)); G.numENode=0;//当前有效的边 G.numVNode=0;}void InsertVNode(MatGraph &G,char V)//MatG插入V { G.VNode[G.numVNode++]=V;}void InsertENode(MatGraph &G,char V,char W,int Weight)//有向图MatG插入边 { //有向图 int v1,v2;//边VW对应下标v1,v2 for(int i=0;i<G.numVNode;i++) { if(G.VNode[i]==V) v1=i; if(G.VNode[i]==W) v2=i; } G.ENode[v1][v2]=Weight; /*//若是无向图 G.ENode[v2][v1]=Weight; */ G.numENode++;}void Mat_DFS(MatGraph G,int start)//MatGraoh的DFS { cout<<G.VNode[start]; visited[start]=1;//代表已经访问 for(int i=0;i<G.numVNode;i++) { if(G.ENode[start][i]!=INF&&visited[i]!=1) { Mat_DFS(G,i); } }} void Mat_DFS_Tree(MatGraph G,int start)//MatGraoh的DFS { for(int i=0;i<G.numVNode;i++) { if(G.ENode[start][i]!=INF&&visited[i]!=1) { T.node[i].Data = G.VNode[i]; T.node[i].Parent = start; T.numTNode++; visited[start]=1;//代表已经访问 Mat_DFS_Tree(G,i); } }} void Mat_DFS_BianHao(MatGraph G,int start,int count,int arr[])//MatGraoh的DFS { arr[start]=count;//count最初从1开始 visited[start]=1;//代表已经访问 for(int i=0;i<G.numVNode;i++) { if(G.ENode[start][i]!=INF&&visited[i]!=1) { Mat_DFS_BianHao(G,i,++count,arr); } }} void Mat_BFS(MatGraph G,int start){ queue<int>Q; cout<<G.VNode[start]; visited[start]=1; Q.push(start);//进队之前cout while(!Q.empty()) { int v = Q.front(); Q.pop(); for(int i=0;i<G.numVNode;i++) { if(G.ENode[v][i]!=INF&&visited[i]!=1) { cout<<G.VNode[i]; Q.push(i); visited[i]=1; } } }}void Mat_BFS_Tree(MatGraph G,int start){ queue<int>Q; visited[start]=1; Q.push(start);//进队之前cout while(!Q.empty()) { int v = Q.front(); Q.pop(); for(int i=0;i<G.numVNode;i++) { if(G.ENode[v][i]!=INF&&visited[i]!=1) { //cout<<G.VNode[v]<<"->"<<G.VNode[i]<<" "; T.node[i].Data = G.VNode[i]; T.node[i].Parent = v; T.numTNode++; Q.push(i); visited[i]=1; } } }}void Mat_BFS_BianHao(MatGraph G,int start,int count,int arr[]){ queue<int>Q; visited[start]=1; arr[start]=count;//首个count=1 Q.push(start);//进队之前cout while(!Q.empty()) { int v = Q.front(); Q.pop(); for(int i=0;i<G.numVNode;i++) { if(G.ENode[v][i]!=INF&&visited[i]!=1) { arr[i]=++count; Q.push(i); visited[i]=1; } } }}void CreatMatGraph(MatGraph &G) //读入边和顶点建立邻接矩阵 { FILE *p; assert((p=fopen("MatGraph.txt","r"))!=NULL); InitMatGraph(G); int n; fscanf(p,"顶点个数=%d;",&n); for(int i=0;i<n;i++) { char V; fscanf(p,"%c;",&V); InsertVNode(G,V); } int m; fscanf(p,"边个数=%d;",&m); for(int i=0;i<m;i++) { char V1,V2; int weight; fscanf(p,"%c,%c,%d;",&V1,&V2,&weight); InsertENode(G,V1,V2,weight); } fclose(p); ShowMatGraph(G);//进行展示邻接矩阵 }void ShowMatGraph(MatGraph G)//进行展示邻接矩阵 { int n=G.numVNode; int m=G.numENode; //进行展示邻接矩阵 cout<<" ";//保证输入格式 for(int i=0;i<n;i++) cout<<G.VNode[i]<<" "; cout<<endl; for(int i=0;i<n;i++) { cout<<G.VNode[i]<<" "; for(int j=0;j<n;j++) { if(G.ENode[i][j]!=INF) cout<<G.ENode[i][j]<<" "; else cout<<"oo "; } cout<<endl; }}void CreatALGraph(ALGraph* G)//有向图处理 { FILE *p; assert((p=fopen("ALGraph.txt","r"))!=NULL);//txt文本必须要ANSI编码模式才可以中文 fscanf(p,"顶点个数=%d;",&(G->numVNode));//最初就给定numVNode fscanf(p,"边个数=%d;",&(G->numENode)); for(int i=0;i<G->numVNode;i++) { fscanf(p,"%c;",&(G->adjlist[i].Data)); G->adjlist[i].FirstENode=NULL; } for(int i=0 ;i<G->numENode;i++) { char V1,V2; int V1_i,V2_i;//V1对应的下标是V1_i int Weight; fscanf(p,"%c,%c,%d;",&V1,&V2,&Weight); ENode* e; e= new ENode; for(int i=0;i<G->numVNode;i++) { if(V1 == G->adjlist[i].Data) { V1_i=i; } if(V2 == G->adjlist[i].Data ) { V2_i=i; } }//找到下标 //边 V1->V2 e->AdjV=V2_i;//记录e边,由V1指向V2 e->Weight = Weight; e->Next=G->adjlist[V1_i].FirstENode;//e边的起点顶点在V1所以头插法进入V1 G->adjlist[V1_i].FirstENode=e;//头插法 /*//若是无向图 //边V2->V1 e= new ENode; e->AdjV=V1_i; e->Weight = Weight; e->Next=G->adjlist[V2_i].FirstENode; G->adjlist[V2_i].FirstENode = e; */ } fclose(p); ShowALGraph(G);}void ShowALGraph(ALGraph* G)//进行展示邻接表 { for(int i=0;i<G->numVNode;i++) { cout<<G->adjlist[i].Data; ENode* E; E = G->adjlist[i].FirstENode; while(E!=NULL) { cout<<"->"; cout<<E->Weight<<"->"<<G->adjlist[E->AdjV].Data; E=E->Next; } cout<<"->NULL\n"; }}void AL_DFS(ALGraph* G,int v){ cout<<G->adjlist[v].Data; visited[v]=1;//cout后就记录进visited ENode* p; p=G->adjlist[v].FirstENode; while(p!=NULL) { if(visited[p->AdjV]!=1) AL_DFS(G,p->AdjV);//从下一个点以DFS前进 p=p->Next;//当上一条路结束,开始这最开始同一个起点的下一条 }}void AL_DFS_BianHao(ALGraph* G,int v,int count,int arr[]){ arr[v]=count;//首次进入的count=1 visited[v]=1;//cout后就记录进visited ENode* p; p=G->adjlist[v].FirstENode; while(p!=NULL) { if(visited[p->AdjV]!=1) AL_DFS_BianHao(G,p->AdjV,++count,arr);//从下一个点以DFS前进 p=p->Next;//当上一条路结束,开始这最开始同一个起点的下一条 }}void AL_DFS_Tree(ALGraph* G,int v){ //cout<<G->adjlist[v].Data; visited[v]=1;//cout后就记录进visited ENode* p; p=G->adjlist[v].FirstENode; while(p!=NULL) { if(visited[p->AdjV]!=1) { //cout<<G->adjlist[v].Data<<"->"<<G->adjlist[p->AdjV].Data<<" "; T.node[p->AdjV].Data =G->adjlist[p->AdjV].Data; T.node[p->AdjV].Parent = v; T.numTNode++; AL_DFS_Tree(G,p->AdjV);//从下一个点以DFS前进 } p=p->Next;//当上一条路结束,开始这最开始同一个起点的下一条 }}void AL_BFS(ALGraph *G,int v){ cout<<G->adjlist[v].Data; queue<int> Q; Q.push(v);//设置为入队前cout visited[v]=1; while(!Q.empty())// { int temp; temp=Q.front(); Q.pop(); ENode* E = G->adjlist[temp].FirstENode;//E为当前顶点的某条边 while(E!=NULL) { if(visited[E->AdjV]!=1)//边E指向的终点顶点E->AdjV并没有访问过 { cout<<G->adjlist[E->AdjV].Data; visited[E->AdjV]=1;//cout后visited记录 } if (E->Next!=NULL)//和E同一个起点终点的另一条边,所指向的顶点,没有访问过 ,并且这另一条边存在 if(visited[E->Next->AdjV]!=1)//连用两个if 是因为visited[E->Next->AdjV],当 E->Next=NULL时候,判断语句visit[X]溢出 { Q.push(E->AdjV); } E = E->Next;//E更新为同一个出发顶点的下一条边 } }}void AL_BFS_BianHao(ALGraph *G,int v,int count,int arr[]){ arr[v]=count; queue<int> Q; Q.push(v);//设置为入队前cout visited[v]=1; while(!Q.empty())// { int temp; temp=Q.front(); Q.pop(); ENode* E = G->adjlist[temp].FirstENode;//E为当前顶点的某条边 while(E!=NULL) { if(visited[E->AdjV]!=1)//边E指向的终点顶点E->AdjV并没有访问过 { arr[E->AdjV]=++count; visited[E->AdjV]=1;//cout后visited记录 } if (E->Next!=NULL)//和E同一个起点终点的另一条边,所指向的顶点,没有访问过 ,并且这另一条边存在 if(visited[E->Next->AdjV]!=1)//连用两个if 是因为visited[E->Next->AdjV],当 E->Next=NULL时候,判断语句visit[X]溢出 { Q.push(E->AdjV); } E = E->Next;//E更新为同一个出发顶点的下一条边 } }}void AL_BFS_Tree(ALGraph *G,int v){ queue<int> Q; Q.push(v);//设置为入队前cout visited[v]=1; while(!Q.empty())// { int temp; temp=Q.front(); Q.pop(); ENode* E = G->adjlist[temp].FirstENode;//E为当前顶点的某条边 while(E!=NULL) { if(visited[E->AdjV]!=1)//边E指向的终点顶点E->AdjV并没有访问过 { //cout<<G->adjlist[temp].Data<<"->"<<G->adjlist[E->AdjV].Data<<" "; T.node[E->AdjV].Data =G->adjlist[E->AdjV].Data; T.node[E->AdjV].Parent = temp; T.numTNode++; visited[E->AdjV]=1;//cout后visited记录 } if (E->Next!=NULL)//和E同一个起点终点的另一条边,所指向的顶点,没有访问过 ,并且这另一条边存在 if(visited[E->Next->AdjV]!=1)//连用两个if 是因为visited[E->Next->AdjV],当 E->Next=NULL时候,判断语句visit[X]溢出 { Q.push(E->AdjV); } E = E->Next;//E更新为同一个出发顶点的下一条边 } }}void MatToList(MatGraph MatG,ALGraph* &ALG)//同样是做有向图{//已知MatGraph邻接矩阵 有向图 转为 邻接表 ALG = new ALGraph; ALG->numVNode = MatG.numVNode; ALG->numENode = MatG.numENode; for(int i=0;i<MatG.numVNode;i++) ALG->adjlist[i].FirstENode =NULL;//将初始化 for(int i=0;i<MatG.numVNode;i++) { int j =0; ALG->adjlist[i].Data = MatG.VNode[i];//顶点转换 for(;j<MatG.numVNode;j++)//将这个顶点所有相关的边转换 { if(INF!=MatG.ENode[i][j])//发现有对应的边从i到j { ENode* E = new ENode; E->AdjV=j; E->Weight = MatG.ENode[i][j]; E->Next = ALG->adjlist[i].FirstENode; ALG->adjlist[i].FirstENode=E;//头插法 } } } cout<<"已知的邻接矩阵\n"; ShowMatGraph(MatG); cout<<"转换后变成\n"; ShowALGraph(ALG);}void ListToMat(MatGraph &MatG,ALGraph* &ALG)//这里是!做有向图的 ! {//已知邻接表G ,转为邻接矩阵MatG ENode* E; memset(MatG.VNode,'#',sizeof(MatG.VNode)); memset(MatG.ENode,INF,sizeof(MatG.ENode)); MatG.numENode=ALG->numENode; MatG.numVNode=ALG->numVNode; for(int i=0;i<ALG->numVNode;i++) { E = ALG->adjlist[i].FirstENode; MatG.VNode[i] = ALG->adjlist[i].Data;//转换顶点 while(E!=NULL) {//转换边 MatG.ENode[i][E->AdjV]=E->Weight; E=E->Next; } } cout<<"已知的邻接表\n"; ShowALGraph(ALG); cout<<"转换后变成\n"; ShowMatGraph(MatG);}void AL_DFS_FeiDiGui(ALGraph* ALG,int start){ stack<ENode*> S; memset(visited,0,sizeof(visited)); cout<<ALG->adjlist[start].Data; visited[start]=1;//访问之后立刻visited等于1 ENode* E = ALG->adjlist[start].FirstENode; while(E||!S.empty()) { while(E) { //1.更深 if(visited[E->AdjV]==0)//如果更深的终点顶点没有访问过 { S.push(E);//visited之前入队,留下标记可以返回 cout<<ALG->adjlist[E->AdjV].Data; visited[E->AdjV]=1;//cout之后立刻visited E = ALG->adjlist[E->AdjV].FirstENode;//进入更深的终点顶点 } //2.转向 else//visited[E->Adjv]==1 { E = E->Next; }//转为同一个起点的另一条边 } if(!S.empty())//3.这条路全验证不通之后返回 { E = S.top(); S.pop(); } }}void Mat_DFS_FeiDiGui(MatGraph MatG,int start){ stack<int> S; cout<<MatG.VNode[start]; visited[start]=1; S.push(start);//visited之后就立刻入栈 //这里选择对首元素进行while之前就入栈int v1_i;int v2_i; while(!S.empty()) { v1_i = S.top(); for(v2_i=0;v2_i<MatG.numVNode;v2_i++) {//1.深入 if(visited[v2_i]!=1&&MatG.ENode[v1_i][v2_i]!=INF)//v1->v2有边且从来没有访问过 { cout<<MatG.VNode[v2_i]; visited[v2_i]=1;// S.push(v2_i);//cout和visited后,入栈方便返回 //并且会由v2_i充当新的v1_i准备继续深入 break;//准备继续深入 } //2.转向,通过for循环已经做到 }//3.返回上一级 if(v2_i==MatG.numVNode && !S.empty()) {//证明由v1_i已经不可能再深入了 S.pop(); } }}void InitialTree()//初始化树 { for(int i=0;i<MaxVNode;i++) { T.node[i].Data = '#'; T.node[i].Parent = -1; } T.numTNode=0;}void ShowTree(){ for(int i=0;i<MaxVNode;i++) { if(T.node[i].Data!='#') cout<<"T.node["<<i<<"].Data ="<<T.node[i].Data<<" ,Parent="<<T.node[i].Parent<<endl; }}int main(){ cout<<" -------------------------------------------------------------------\n";cout<<" |================== 图的相关操作 =================|\n";cout<<" -------------------------------------------------------------------\n\n";cout<<" |================== 1.根据txt建立邻接矩阵 =================|\n";cout<<" |================== 2.邻接矩阵DFS =================|\n";cout<<" |================== 3.邻接矩阵BFS =================|\n";cout<<" |================== 4.根据txt建立邻接表 =================|\n";cout<<" |================== 5.邻接表DFS =================|\n";cout<<" |================== 6.邻接表BFS =================|\n";cout<<" |================== 7.邻接矩阵转邻接表 =================|\n"; cout<<" |================== 8.邻接表转邻接矩阵 =================|\n";cout<<" |================== 9.邻接表非递归DFS =================|\n";cout<<" |================== 10.邻接矩阵非递归DFS =================|\n"; while(1) { int n; cout<<"请输入"; cin>>n; int start; int arr[MaxVNode]; switch(n) { case 1: MatGraph MatG; CreatMatGraph(MatG); cout<<endl; break; case 2: cout<<"请输入从哪个下标开始进行:"; cin>>start; // memset(visited,0,sizeof(visited));//初始化visited cout<<"序列展示\n" ; Mat_DFS(MatG,start); cout<<endl; // memset(visited,0,sizeof(visited));//初始化visited InitialTree(); cout<<"生成树展示\n"; Mat_DFS_Tree(MatG,start); //补充特例头结点进入 T.node[start].Data= MatG.VNode[start]; T.node[start].Parent = -1; T.numTNode++; ShowTree(); cout<<endl; // memset(visited,0,sizeof(visited));//初始化visited memset(arr,-1,sizeof(arr));//初始化arr cout<<"编号展示\n"; Mat_DFS_BianHao(MatG,start,1,arr);//MatGraoh的DFS for(int i=0;i<MatG.numVNode;i++) cout<<MatG.VNode[i]<<":"<<arr[i]<<" "; cout<<endl; break; case 3: cout<<"请输入从哪个下标开始进行:"; cin>>start; cout<<"序号展示"; memset(visited,0,sizeof(visited));//初始化visited Mat_BFS(MatG,start); cout<<endl; memset(visited,0,sizeof(visited));//初始化visited InitialTree(); cout<<"生成树展示\n"; Mat_BFS_Tree(MatG,start); //补充特例头结点进入 T.node[start].Data= MatG.VNode[start]; T.node[start].Parent = -1; T.numTNode++; ShowTree(); cout<<endl; memset(visited,0,sizeof(visited));//初始化visited cout<<"编号展示\n"; Mat_BFS_BianHao(MatG,start,1,arr);//MatGraoh的DFS for(int i=0;i<MatG.numVNode;i++) cout<<MatG.VNode[i]<<":"<<arr[i]<<" "; cout<<endl; break; case 4: ALGraph* ALG; ALG = new ALGraph; CreatALGraph(ALG); cout<<endl; break; case 5: cout<<"请输入从哪个下标开始进行:"; cin>>start; cout<<"序号展示\n"; memset(visited,0,sizeof(visited));//初始化visited AL_DFS(ALG,start); cout<<endl; memset(visited,0,sizeof(visited));//初始化visited InitialTree(); cout<<"生成树展示\n"; AL_DFS_Tree(ALG,start); //补充特例头结点进入 T.node[start].Data= ALG->adjlist[start].Data; T.node[start].Parent = -1; T.numTNode++; ShowTree(); cout<<endl; memset(visited,0,sizeof(visited));//初始化visited memset(arr,-1,sizeof(arr));//初始化arr cout<<"编号展示\n"; AL_DFS_BianHao(ALG,start,1,arr);//MatGraoh的DFS ,count=1所以直接传值1 for(int i=0;i<ALG->numVNode;i++) cout<<ALG->adjlist[i].Data<<":"<<arr[i]<<" "; cout<<endl; break; case 6: cout<<"请输入从哪个下标开始进行:"; cin>>start; cout<<"序号展示\n"; memset(visited,0,sizeof(visited));//初始化visited AL_BFS(ALG,start); cout<<endl; memset(visited,0,sizeof(visited));//初始化visited InitialTree(); cout<<"生成树展示\n"; AL_BFS_Tree(ALG,start); //补充特例头结点进入 T.node[start].Data= ALG->adjlist[start].Data; T.node[start].Parent = -1; T.numTNode++; ShowTree(); cout<<endl; memset(visited,0,sizeof(visited));//初始化visited memset(arr,-1,sizeof(arr));//初始化arr cout<<"编号展示\n"; AL_BFS_BianHao(ALG,start,1,arr);//MatGraoh的DFS ,count=1所以直接传值1 for(int i=0;i<ALG->numVNode;i++) cout<<ALG->adjlist[i].Data<<":"<<arr[i]<<" "; cout<<endl; break; case 7: memset(visited,0,sizeof(visited));//初始化visited MatToList(MatG,ALG); cout<<endl; break; case 8: memset(visited,0,sizeof(visited));//初始化visited ListToMat(MatG,ALG); cout<<endl; break; case 9: memset(visited,0,sizeof(visited));//初始化visited cout<<"请输入从哪个下标开始进行:"; cin>>start; AL_DFS_FeiDiGui(ALG,start); cout<<endl; break; case 10: memset(visited,0,sizeof(visited));//初始化visited cout<<"请输入从哪个下标开始进行:"; cin>>start; Mat_DFS_FeiDiGui(MatG,start); cout<<endl; break; } } }