Loading... # 试验8.1 存储结构转换问题 ## 1.问题描述 以图的邻接矩阵方式创建一有向图,然后根据此存储,求图的邻接表的存储方式,并输出邻接表。 ## 2.数据结构设计 分为图的邻接矩阵存储和图的邻接表存储 ### 2.1 存储设计 * 图的邻接矩阵`AdjMatrix.h` ```c++ #define MAX_VERTEX_NUM 20 const int infinity = INT_MAX; struct ArcCell { int adj;//边权 }; template<class T> struct _MGraph { T vexs[MAX_VERTEX_NUM];//顶点表 ArcCell arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵 int vexnum;//顶点数 int arcnum;//边数 }; ``` * 图的邻接表`AdjList.h` ```c++ #define MAX_VERTEX_NUM 20 struct ArcNode { int adjvex;//该弧所指向的顶点的位置 struct ArcNode* nextarc;//指向下一条弧的指针 //int* info;//该弧相关信息的指针,有向图不使用 }; template<class T> struct VNode { T data;//顶点信息 ArcNode* firstarc;//指向第一条依附该顶点的指针 }; template<class T> struct _ALGraph { VNode<T>vertices[MAX_VERTEX_NUM]; int vexnum;//顶点数 int arcnum;//边数 }; ``` ### 2.2 函数设计 * 图的邻接矩阵:构造有向图、定位顶点、输出邻接矩阵 ```c++ template<class T> class MGraph { public: _MGraph<T>mgraph; bool visited[MAX_VERTEX_NUM]; void CreateDG();//构造有向图 int LocateVex(T u);//图存在,途中存在顶点u,则返回该顶点在图中的位置 void DisPlay();//输出邻接矩阵 }; ``` * 图的邻接表:邻接矩转换、输出邻接表 ```c++ template<class T> class ALGraph { _ALGraph<T>algraph; bool visited[MAX_VERTEX_NUM]; public: void Display();//输出邻接表 void AdjMatrixToAdjList(MGraph<T> mg);//邻接矩阵转换为邻接表 }; ``` ## 3.算法设计 ### 3.1 邻接矩阵 ##### 3.1.1 创建邻接矩阵 ```c++ template<class T> void MGraph<T>::CreateDG() { int i, j; T v1, v2; cout << "请输入有项图的顶点个数,弧的个数:"; cin >> mgraph.vexnum >> mgraph.arcnum; cout << "请输入各个顶点:"; for (i = 0; i < mgraph.vexnum; i++)//构造顶点向量 cin >> mgraph.vexs[i]; for (i = 0; i < mgraph.vexnum; i++)//初始化邻接矩阵 { for (j = 0; j < mgraph.vexnum; j++) { mgraph.arcs[i][j].adj = 0; } } for (i = 0; i < mgraph.arcnum; i++)//构造邻接矩阵 { cout << "请输入一条弧的起点,终点:"; cin >> v1 >> v2; int m = LocateVex(v1); int n = LocateVex(v2); mgraph.arcs[m][n].adj = 1; } } template<class T> int MGraph<T>::LocateVex(T u) { for (int i = 0; i < MAX_VERTEX_NUM; i++) { if (u == mgraph.vexs[i]) { return i; } } return -1; } ``` #### 3.1.2 输出邻接矩阵 ```c++ template<class T> void MGraph<T>::DisPlay() { int i, j; cout << mgraph.vexnum << "个顶点" << mgraph.arcnum << "条边"; cout << "顶点为:"; for (i = 0; i < mgraph.vexnum; i++) cout << mgraph.vexs[i] << " "; cout << endl; for (i = 0; i < mgraph.vexnum; i++) { for (j = 0; j < mgraph.vexnum; j++) { cout << setw(5) << mgraph.arcs[i][j].adj << "\t"; } cout << endl; } } ``` ### 3.2 图的邻接表 #### 3.2.1 邻接矩阵转换 ```c++ template<class T> void ALGraph<T>::AdjMatrixToAdjList(MGraph<T> mg) { int i,j; ArcNode* p; for (i = 0; i < mg.mgraph.vexnum; i++)//邻接表表头向量初始化 { algraph.vertices[i].firstarc = NULL; } for (i = 0; i < mg.mgraph.vexnum; i++)//顶点赋值 { if (mg.mgraph.vexs[i]!="") { T t = mg.mgraph.vexs[i]; algraph.vertices[i].data = t; } } for (i = 0; i <mg.mgraph.vexnum; i++)//遍历邻接矩阵 { for (j = 0; j < mg.mgraph.vexnum; j++) { if (mg.mgraph.arcs[i][j].adj == 1) { p = new ArcNode;//实例化 p->adjvex = j;//p指向的顶点 p->nextarc = algraph.vertices[i].firstarc;//p的下一条弧 algraph.vertices[i].firstarc = p;//链入链表 } } } algraph.vexnum = mg.mgraph.vexnum;//顶点数 algraph.arcnum = mg.mgraph.arcnum;//边数 } ``` #### 3.2.2 邻接表转换 ```c++ template<class T> void ALGraph<T>::Display() { int i; ArcNode* p; cout << algraph.vexnum << "个顶点:" << endl; for (i = 0; i < algraph.vexnum; i++) cout << algraph.vertices[i].data << " "; cout << endl; for (i = 0; i < algraph.vexnum; i++) { p = algraph.vertices[i].firstarc; while (p) { cout << algraph.vertices[i].data << "->" << algraph.vertices[p->adjvex].data << "\t"; cout << endl; p = p->nextarc; } } } ``` ## 4.运行测试 ### 试验一 测试用例:  1. 输入顶点数和边数  2. 输入顶点  3. 输入起点、终点  4. 得到邻接矩阵,并转换为邻接表输出  ### 试验二 测试用例:  输出:  ## 5.调试记录与收获 1. 源程序清单 * `AdjList.h`邻接表 ```c++ #pragma once #include"AdjMatrix.h" #include<iostream> using namespace std; #define MAX_VERTEX_NUM 20 struct ArcNode { int adjvex;//该弧所指向的顶点的位置 struct ArcNode* nextarc;//指向下一条弧的指针 //int* info;//该弧相关信息的指针 }; template<class T> struct VNode { T data;//顶点信息 ArcNode* firstarc;//指向第一条依附该顶点的指针 }; template<class T> struct _ALGraph { VNode<T>vertices[MAX_VERTEX_NUM]; int vexnum;//顶点数 int arcnum;//边数 }; template<class T> class ALGraph { _ALGraph<T>algraph; bool visited[MAX_VERTEX_NUM]; public: void Display();//输出邻接表 void AdjMatrixToAdjList(MGraph<T> mg);//邻接矩阵转换为邻接表 }; template<class T> void ALGraph<T>::Display() { int i; ArcNode* p; cout << algraph.vexnum << "个顶点:" << endl; for (i = 0; i < algraph.vexnum; i++) cout << algraph.vertices[i].data << " "; cout << endl; for (i = 0; i < algraph.vexnum; i++) { p = algraph.vertices[i].firstarc; while (p) { cout << algraph.vertices[i].data << "->" << algraph.vertices[p->adjvex].data << "\t"; cout << endl; p = p->nextarc; } } } template<class T> void ALGraph<T>::AdjMatrixToAdjList(MGraph<T> mg) { int i,j; ArcNode* p; for (i = 0; i < mg.mgraph.vexnum; i++)//邻接表表头向量初始化 { algraph.vertices[i].firstarc = NULL; } for (i = 0; i < mg.mgraph.vexnum; i++)//顶点赋值 { if (mg.mgraph.vexs[i]!="") { T t = mg.mgraph.vexs[i]; algraph.vertices[i].data = t; } } for (i = 0; i <mg.mgraph.vexnum; i++)//遍历邻接矩阵 { for (j = 0; j < mg.mgraph.vexnum; j++) { if (mg.mgraph.arcs[i][j].adj == 1) { p = new ArcNode;//实例化 p->adjvex = j;//p指向的顶点 p->nextarc = algraph.vertices[i].firstarc;//p的下一条弧 algraph.vertices[i].firstarc = p;//链入链表 } } } algraph.vexnum = mg.mgraph.vexnum;//顶点数 algraph.arcnum = mg.mgraph.arcnum;//边数 } ``` * `AdjMatrix.h`邻接矩阵 ```c++ #pragma once #include<iostream> #include<iomanip> using namespace std; #define MAX_VERTEX_NUM 20 const int infinity = INT_MAX; struct ArcCell { int adj;//边权 //char* info;//该弧的相关信息 }; template<class T> struct _MGraph { T vexs[MAX_VERTEX_NUM];//顶点表 ArcCell arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵 int vexnum;//顶点数 int arcnum;//边数 }; template<class T> class MGraph { public: _MGraph<T>mgraph; bool visited[MAX_VERTEX_NUM]; void CreateDG();//构造有向图 int LocateVex(T u);//图存在,途中存在顶点u,则返回该顶点在图中的位置 void DisPlay();//输出邻接矩阵 }; template<class T> void MGraph<T>::CreateDG() { int i, j; T v1, v2; cout << "请输入有项图的顶点个数,弧的个数:"; cin >> mgraph.vexnum >> mgraph.arcnum; cout << "请输入各个顶点:"; for (i = 0; i < mgraph.vexnum; i++)//构造顶点向量 cin >> mgraph.vexs[i]; for (i = 0; i < mgraph.vexnum; i++)//初始化邻接矩阵 { for (j = 0; j < mgraph.vexnum; j++) { mgraph.arcs[i][j].adj = 0; } } for (i = 0; i < mgraph.arcnum; i++)//构造邻接矩阵 { cout << "请输入一条弧的起点,终点:"; cin >> v1 >> v2; int m = LocateVex(v1); int n = LocateVex(v2); mgraph.arcs[m][n].adj = 1; } } template<class T> int MGraph<T>::LocateVex(T u) { for (int i = 0; i < MAX_VERTEX_NUM; i++) { if (u == mgraph.vexs[i]) { return i; } } return -1; } template<class T> void MGraph<T>::DisPlay() { int i, j; cout << mgraph.vexnum << "个顶点" << mgraph.arcnum << "条边"; cout << "顶点为:"; for (i = 0; i < mgraph.vexnum; i++) cout << mgraph.vexs[i] << " "; cout << endl; for (i = 0; i < mgraph.vexnum; i++) { for (j = 0; j < mgraph.vexnum; j++) { cout << setw(5) << mgraph.arcs[i][j].adj << "\t"; } cout << endl; } } ``` * `main.cpp`主函数 ```c++ #include<iostream> #include"AdjList.h" #include"AdjMatrix.h" using namespace std; int main() { MGraph<string> g1; g1.CreateDG(); g1.DisPlay(); ALGraph<string> g2; g2.AdjMatrixToAdjList(g1); g2.Display(); } ``` # 实验8.2 有向图的路径问题 ## 1.问题描述 对于有向图$G=(V,E)$,任意$v_i,v_j\in(v_i\neq{v_j})$,判断从顶点$v_i$到顶点$v_j$是否存在路径 ## 2.数据结构设计 ### 2.1 结构设计 使用有向图的邻接表表示构造有向图,使用栈存储 #### 2.1.1 有向图 ```c++ struct ArcNode { int adjvex;//该弧所指向的顶点的位置 struct ArcNode* nextarc;//指向下一条弧的指针 //int* info;//该弧相关信息的指针 }; template<class T> struct VNode { T data;//顶点信息 ArcNode* firstarc;//指向第一条依附该顶点的指针 }; template<class T> struct _ALGraph { VNode<T>vertices[MAX_VERTEX_NUM]; int vexnum;//顶点数 int arcnum;//边数 }; ``` #### 2.1.2 栈 ```C++ template<class T> class SqStack { private: T* base; //栈底指针 int top; //栈顶 int stacksize; //栈容量 }; ``` ### 2.2 函数设计 #### 2.2.1 有向图 通过键盘输入创建有向图,输出邻接表,获取路径 ```c++ template<class T> class ALGraph { _ALGraph<T>algraph; bool visited[MAX_VERTEX_NUM]; public: int LocateVex(T u);//返回顶点u在图中的位置 void Display();//输出邻接表 void CreateGraph();//创建图 T GetVex(int index);//index是图中某个顶点的序号,返回v的值 int FirstAdjVex(T v);//返回v的第一个邻接点的序号,若无邻接点,则返回空 int NextAdjVex(T v, T w);//返回v相对于w的下一个邻接点的序号,若w是v的最后一个邻接点,则返回空 void Pathitoj(T vi, T vj, SqStack<T> stack,int *isfind);//寻找vi,vj之间的路径 ``` #### 2.2.1 栈 需要使用栈来保存深度遍历的顶点,所以需要使用:创建栈,入栈,出栈,输出栈 ```c++ template<class T> class SqStack { private: T* base; //栈底指针 int top; //栈顶 int stacksize; //栈容量 public: SqStack(int m); //构造函数 void Push(T x); //入栈 T Pop(); //出栈 T GetTop(); //获取栈顶元素 int StackEmpty(); //测栈空 void Display(); //显示栈中元素 }; ``` ## 3.算法设计 ### 3.1 有向图 #### 3.1.1 创建有向图 ```c++ template<class T> void ALGraph<T>::CreateGraph() { int i, j, k; T v1, v2; cout << "请输入图的顶点数,边数:"; cin >> algraph.vexnum >> algraph.arcnum; cout << "请输入" << algraph.vexnum << "个顶点的值:"; for (i = 0; i < algraph.vexnum; i++) { cin >> algraph.vertices[i].data; algraph.vertices[i].firstarc = NULL; } cout << "请输入每条弧的弧尾、弧头!" << endl; for (k = 0; k < algraph.arcnum; k++) { cout << "请输入一条弧的弧尾、弧头:"; cin >> v1 >> v2; i = LocateVex(v1); j = LocateVex(v2); ArcNode* p1 = new ArcNode; p1->adjvex = i; p1->nextarc = algraph.vertices[j].firstarc; algraph.vertices[j].firstarc = p1; } for (int i = 0; i < MAX_VERTEX_NUM; i++) { visited[i] = false; } } ``` #### 3.1.2 输出邻接表 ```c++ template<class T> void ALGraph<T>::Display() { int i; ArcNode* p; cout << algraph.vexnum << "个顶点:" << endl; for (i = 0; i < algraph.vexnum; i++) cout << algraph.vertices[i].data << " "; cout << endl; for (i = 0; i < algraph.vexnum; i++) { p = algraph.vertices[i].firstarc; while (p) { cout << algraph.vertices[i].data << "->" << algraph.vertices[p->adjvex].data << "\t"; cout << endl; p = p->nextarc; } } } ``` #### 3.1.3 判断路径 ```c++ template<class T> void ALGraph<T>::Pathitoj(T vi, T vj, SqStack<T> stack,int *isfind) {//传入判断标记isfind int i; int j = LocateVex(vi); stack.Push(vi); visited[j] = true;//设置已访问点 vi = GetVex(j); for (i = FirstAdjVex(vi); i >= 0; i = NextAdjVex(vi, GetVex(i))) { if (!visited[i] && GetVex(i) != vj) { Pathitoj(GetVex(i), vj, stack,isfind);//继续深度搜索 } else if (GetVex(i) == vj) { cout << vj; stack.Display();//输出路径 *isfind = 1; } else if (visited[i]) { stack.Pop(); } } } ``` ### 3.2 栈 #### 3.2.1 构造 ```c++ template<class T> SqStack<T>::SqStack(int m) { base = new T[m]; if (base == NULL) { cout << "栈创建失败,退出!" << endl; exit(1); } stacksize = m; top = -1; } ``` #### 3.2.2 入栈 ```c++ template<class T> void SqStack<T>::Push(T x) { if (top == stacksize - 1) throw "栈满,无法入栈"; top++; base[top] = x; } ``` #### 3.2.3 出栈 ```c++ template<class T> T SqStack<T>::Pop() { T x; if (top == -1)throw"栈空,不能出栈"; x = base[top--]; return x; } ``` #### 3.2.4 遍历栈 ```c++ template<class T> void SqStack<T>::Display() { int i = top; while (i >= 0) cout << "<-" <<base[i--]; cout << endl; } ``` ### 3.3 主函数 输入需要判断的顶点,并输出信息 ```c++ ALGraph<string> a1; a1.CreateGraph(); //创建有向图 a1.Display();//输出有向图 cout << "依次输入两个顶点"; string vi, vj; cin >> vi >> vj;//输入需要判断的顶点 SqStack<string>stack(20); int isfind=0; a1.Pathitoj(vi, vj, stack, &isfind);//判断路径 if (isfind) { cout << "存在路径!"; } else { cout << "不存在路径!"; } ``` ## 4.运行和测试 - 测试一 使用如下弱连通图测试  1. 输入   2. 输入需要测试的路径B->C  3. 获得输出  测试B->A  - 测试二:强连通图  1. 测试A->C  2. 测试B->C  ## 5.调试记录与收获 1. 源程序清单 - 邻接表`ADjList.h` ```c++ #pragma once #include<iostream> #include"SqStack.h" using namespace std; #define MAX_VERTEX_NUM 20 struct ArcNode { int adjvex;//该弧所指向的顶点的位置 struct ArcNode* nextarc;//指向下一条弧的指针 //int* info;//该弧相关信息的指针 }; template<class T> struct VNode { T data;//顶点信息 ArcNode* firstarc;//指向第一条依附该顶点的指针 }; template<class T> struct _ALGraph { VNode<T>vertices[MAX_VERTEX_NUM]; int vexnum;//顶点数 int arcnum;//边数 }; template<class T> class ALGraph { _ALGraph<T>algraph; bool visited[MAX_VERTEX_NUM]; public: int LocateVex(T u);//返回顶点u在图中的位置 void Display();//输出邻接表 void CreateGraph();//创建图 T GetVex(int index);//index是图中某个顶点的序号,返回v的值 int FirstAdjVex(T v);//返回v的第一个邻接点的序号,若无邻接点,则返回空 int NextAdjVex(T v, T w);//返回v相对于w的下一个邻接点的序号,若w是v的最后一个邻接点,则返回空 void Pathitoj(T vi, T vj, SqStack<T> stack,int *isfind);//寻找vi,vj之间的路径 }; template<class T> int ALGraph<T>::LocateVex(T u) { for (int i = 0; i < algraph.vexnum; i++) { if (algraph.vertices[i].data == u) { return i; } } return -1; } template<class T> void ALGraph<T>::Display() { int i; ArcNode* p; cout << algraph.vexnum << "个顶点:" << endl; for (i = 0; i < algraph.vexnum; i++) cout << algraph.vertices[i].data << " "; cout << endl; for (i = 0; i < algraph.vexnum; i++) { p = algraph.vertices[i].firstarc; while (p) { cout << algraph.vertices[i].data << "->" << algraph.vertices[p->adjvex].data << "\t"; cout << endl; p = p->nextarc; } } } template<class T> void ALGraph<T>::CreateGraph() { int i, j, k; T v1, v2; cout << "请输入图的顶点数,边数:"; cin >> algraph.vexnum >> algraph.arcnum; cout << "请输入" << algraph.vexnum << "个顶点的值:"; for (i = 0; i < algraph.vexnum; i++) { cin >> algraph.vertices[i].data; algraph.vertices[i].firstarc = NULL; } cout << "请输入每条弧的弧尾、弧头!" << endl; for (k = 0; k < algraph.arcnum; k++) { cout << "请输入一条弧的弧尾、弧头:"; cin >> v1 >> v2; i = LocateVex(v1); j = LocateVex(v2); ArcNode* p1 = new ArcNode; p1->adjvex = i; p1->nextarc = algraph.vertices[j].firstarc; algraph.vertices[j].firstarc = p1; } for (int i = 0; i < MAX_VERTEX_NUM; i++) { visited[i] = false; } //stack = new SqStack<T>(MAX_VERTEX_NUM); } template<class T> T ALGraph<T>::GetVex(int index) { if (index < 0 || index >= algraph.vexnum) return NULL; return algraph.vertices[index].data; } template<class T> int ALGraph<T>::FirstAdjVex(T v) { int i = LocateVex(v); ArcNode* p = algraph.vertices[i].firstarc; if (p) return p->adjvex; else return -1; } template<class T> int ALGraph<T>::NextAdjVex(T v, T w) { ArcNode* p; int i = LocateVex(v); int j = LocateVex(w); p = algraph.vertices[i].firstarc;//让p指向顶点w while (p && (p->adjvex != j)) { p = p->nextarc; } if (!p || !p->nextarc)//没找到w或者w是最后一个顶点 return -1; else//找到w且w不是最后一个顶点 return p->nextarc->adjvex; } template<class T> void ALGraph<T>::Pathitoj(T vi, T vj, SqStack<T> stack,int *isfind) { int i; int j = LocateVex(vi); stack.Push(vi); //stack.Display(); visited[j] = true; //cout << algraph.vertices[j].data; vi = GetVex(j); for (i = FirstAdjVex(vi); i >= 0; i = NextAdjVex(vi, GetVex(i))) { if (!visited[i] && GetVex(i) != vj) { Pathitoj(GetVex(i), vj, stack,isfind); } else if (GetVex(i) == vj) { cout << vj; stack.Display(); *isfind = 1; } else if (visited[i]) { stack.Pop(); } } } ``` - 栈`SqStack.h` ```c++ #pragma once #include<iostream> using namespace std; template<class T> class SqStack { private: T* base; //栈底指针 int top; //栈顶 int stacksize; //栈容量 public: SqStack(int m); //构造函数 void Push(T x); //入栈 T Pop(); //出栈 T GetTop(); //获取栈顶元素 int StackEmpty(); //测栈空 void Display(); //显示栈中元素 }; template<class T> SqStack<T>::SqStack(int m) { base = new T[m]; if (base == NULL) { cout << "栈创建失败,退出!" << endl; exit(1); } stacksize = m; top = -1; } template<class T> void SqStack<T>::Push(T x) { if (top == stacksize - 1) throw "栈满,无法入栈"; top++; base[top] = x; } template<class T> T SqStack<T>::Pop() { T x; if (top == -1)throw"栈空,不能出栈"; x = base[top--]; return x; } template<class T> T SqStack<T>::GetTop() { if (top == -1) throw "空栈,栈顶无元素"; return base[top]; } template<class T> int SqStack<T>::StackEmpty() { if (top == -1) return 0; else return 1; } template<class T> void SqStack<T>::Display() { int i = top; while (i >= 0) cout <<"<-" <<base[i--] ; cout << endl; } ``` - 主函数`main.cpp` ```c++ #include<iostream> #include"AdjList.h" using namespace std; int main() { ALGraph<string> a1; a1.CreateGraph(); a1.Display(); cout << "依次输入两个顶点"; string vi, vj; cin >> vi >> vj; SqStack<string>stack(20); int isfind=0; a1.Pathitoj(vi, vj, stack, &isfind); if (isfind) { cout << "存在路径!"; } else { cout << "不存在路径!"; } } ``` 最后修改:2022 年 06 月 03 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏