1.问题描述
通过实验达到:
- 加深对图的概念、基本运算的理解;
- 熟练掌握图的逻辑结构与物理结构的关系;
- 以邻接表作为物理结构,熟练掌握图基本运算的实现。
1.1 具体问题
依据最小完备性和常用性相结合的原则,以函数形式定义了创建图、销毁图、查找顶点、获得顶点值和顶点赋值等 12 种基本运算。具体运算功能定义和说明如下。具体运算功能定义如下。
(1) 创建图:函数名称是CreateCraph(G,V,VR);初始条件是V是图的顶点集,VR是图的关系集;操作结果是按V和VR的定义构造图G。
注:①要求图G中顶点关键字具有唯一性。后面各操作的实现,也都要满足一个图中关键字的唯一性,不再赘述;② V和VR对应的是图的逻辑定义形式,比如V为顶点序列,VR为关键字对的序列。不能将邻接矩阵等物理结构来代替V和VR。
(2) 销毁图:函数名称是DestroyGraph(G);初始条件图G已存在;操作结果是销毁图G。
(3) 查找顶点:函数名称是LocateVex(G,u);初始条件是图G存在,u是和G中顶点关键字类型相同的给定值;操作结果是若u在图G中存在,返回关键字为u的顶点位置信息,否则返回其它信息。
(4) 顶点赋值:函数名称是PutVex (G,u,value);初始条件是图G存在,u是和G中顶点关键字类型相同的给定值;操作结果是对关键字为u的顶点赋值value。
(5) 获得第一邻接点:函数名称是FirstAdjVex(G, u);初始条件是图G存在,u是G中顶点的位序;操作结果是返回u对应顶点的第一个邻接顶点位置信息,如果u的顶点没有邻接顶点,否则返回其它表示“空”的信息。
(6) 获得下一邻接点:函数名称是NextAdjVex(G, v, w);初始条件是图G存在,v和w是G中两个顶点的位序,v对应G的一个顶点,w对应v的邻接顶点;操作结果是返回v的(相对于w)下一个邻接顶点的位置信息,如果w是最后一个邻接顶点,返回其它表示“空”的信息。
(7) 插入顶点:函数名称是InsertVex(G,v);初始条件是图G存在,v和G中的顶点具有相同特征;操作结果是在图G中增加新顶点v。(在这里也保持顶点关键字的唯一性)
(8) 删除顶点:函数名称是DeleteVex(G,v);初始条件是图G存在,v是G的一个顶点;操作结果是在图G中删除顶点v和与v相关的弧。
(9) 插入弧:函数名称是InsertArc(G,v,w);初始条件是图G存在,v、w是G的顶点;操作结果是在图G中增加弧<v,w>,如果图G是无向图,还需要增加<w,v>。
(10) 删除弧:函数名称是DeleteArc(G,v,w);初始条件是图G存在,v、w是G的顶点;操作结果是在图G中删除弧<v,w>,如果图G是无向图,还需要删除<w,v>。
(11) 深度优先搜索遍历:函数名称是DFSTraverse(G,visit());初始条件是图G存在;操作结果是图G进行深度优先搜索遍历,依次对图中的每一个顶点使用函数visit访问一次,且仅访问一次。
(12) 广深度优先搜索遍历:函数名称是BFSTraverse(G,visit());初始条件是图G存在;操作结果是图G进行广度优先搜索遍历,依次对图中的每一个顶点使用函数visit访问一次,且仅访问一次。
2. 系统设计
2.1 总体设计
- 使用一个大的 while 语句实现整体功能;
- 使用 switch 语句实现菜单栏的操作选择;
- 在每个 case 语句先进行对图是否存在的判断;
2.2 算法设计
(1)CreateGraph(ALGraph* G);
设计思路:创建图。开辟一个图的存储空间,新建一个顶点并对其进行初始化。
(2)DestroyGraph(ALGraph* G);
设计思路:销毁图。遍历邻接表的每个顶点和弧进行空间释放,最后将全局变量isNull(判定表是否存在)改为TRUE值;
(3)LocateVex(ALGraph G,KeyType u);
设计思路:查找顶点。遍历邻接表内的头结点,按顺序查找与输入值相对应的顶点;
(4)PutVex(ALGraph* G,KeyType u,ValueType v);
设计思路:顶点赋值。遍历邻接表内的顶点,按顺序查找与输入值相对应的头结点,如找到则对该头结点进行赋值操作,否则返回查找失败;
(5)FirstAdjVex(ALGraph G, int index);
设计思路:获得第一邻接点。输入位序,判断输入是否在表长范围内,然后直接用if语句判断该位序的顶点的第一邻接点是否存在,若存在则返回第一邻接点的顶点信息;
(6)NextAdjVex(ALGraph G, int v, int w);
设计思路:获得下一邻接点。遍历邻接表内的头结点,按顺序查找与输入值v相对应的头结点,从该顶点的邻接弧中逐个遍历,若找到邻接顶点w且其下一邻接点存在,则返回下一邻接点的顶点信息,否则返回查找失败;
(7)InsertVex(ALGraph* G, VertexType vnode);
设计思路:插入顶点。若检测到输入的关键字已存在,则返回错误;否则新建一个头结点;
(8)DeleteVex(ALGraph* G, KeyType vnode);
设计思路:删除顶点。首先查找对应关键字的顶点,若找到则在顺序表内删除头结点和以其为弧尾的表结点,然后遍历每个顶点和相关的弧,找到以该顶点为弧头的表结点并删除弧;
(9)InsertArc(ALGraph* G, KeyType v, KeyType w);
设计思路:插入弧。首先定位v和w在头结点中都存在;然后找到弧尾v所在的头结点,遍历到表结点的最尾,若未找到已有的弧(v,w),则在最尾插入弧头结点w,否则返回插入失败;www.biyezuopin.vip
(10)DeleteArc(ALGraph* G, KeyType v, KeyType w);
设计思路:删除弧。首先定位v和w在头结点中都存在,然后找到弧尾v所在的头结点,遍历到表结点的最尾,若找到已有的弧(v,w),则删除该弧,否则返回删除失败;
(11)DFSTraverse(ALGraph* G);
设计思路:深度优先搜索遍历。对每个顶点打上标记,对未遍历的顶点利用递归进行深度优先搜索遍历;
(12)BFSTraverse(ALGraph* G);
设计思路:利用队列完成广度优先搜索遍历,将每个顶点及所对应的弧入队,然后根据出队的顶点顺序,对这些顶点的表结点进行入队,直到队列为空。
3.系统实现
3.1 函数实现
-
CreateGraph:输入顶点的 key 值(整形,唯一确定一个顶点),和该结点所携带的信息,如果输入无误则新建一个头结点放入顺序表中,并设置它的第一邻接点为 NULL;
-
DestroyGraph:遍历邻接表的每个顶点和弧进行空间释放,最后将全局变量 isNull(判定表是否存在)改为 TRUE 值;
-
LocateVex: 遍历邻接表内的顺序表,当遍历到的头结点的关键字与输入值 u 相同时,则返回当前头结点的位序,否则返回 ERROR;
-
PutVex:遍历邻接表内的顺序表,当遍历到的头结点的关键字与输入值 u 相同时,则对该头结点的结点信息进行赋值操作并返回 OK,否则返回 ERROR;
-
FirstAdjVex:输入位序,判断输入是否在表长范围内,然后直接用 if 语句判断该位序的顶点的第一邻接点是否存在,若存在则返回第一邻接点的顶点信息;否则返回 ERROR;
-
NextAdjVex:遍历邻接表内的顺序表,当遍历到的头结点的关键字与输入值 u 相同时,从该顶点的邻接弧中逐个遍历,若找到邻接顶点 w 且其下一邻接点存在,则返回下一邻接点的顶点信息,否则返回 ERROR;
-
InsertVex:若检测到输入的关键字已存在,则输入有误,返回菜单页面;否则在顺序表最后新建一个头结点,对其进行初始化赋值,同时表 G 的 vexnum(顶点数)加一;
-
DeleteVex:首先查找对应关键字的顶点,若找不到则返回查找失败;若找到顶点则在顺序表内删除此头结点和所有以其为弧尾的表结点;然后遍历每个顶点和相关的弧,找到以该顶点为弧头的表结点并删除这些弧;每删除一个顶点,全局变量 DeleteNum 加一;删除成功则同时表 G 的 vexnum(顶点数)减一并返回 OK;
-
InsertArc:首先调用 LocataVex 函数判断 v 和 w 在头结点中是否存在,若有一个不存在就返回 ERROR;然后找到弧尾 v 所在的头结点,遍历到表结点的最尾,若未找到已有的弧(v,w),则在最尾插入表结点 w,邻接表 G 的 arcnum(弧的数量)加一返回 OK;否则打印该弧已存在并返回 ERROR;
-
DeleteArc:首先调用 LocataVex 函数判断 v 和 w 在头结点中是否存在,若有一个不存在就返回 ERROR;然后找到弧尾 v 所在的头结点,遍历到表结点的最尾,若找到已有的弧(v,w),则通过链表操作删除该弧,邻接表 G 的 arcnum(弧的数量)减一并返回 OK;否则返回 ERROR;
-
DFSTraverse:先把邻接表 G 内每个头结点的 book 值置零(用于判断是否遍历过该顶点);从位序为 0 的头结点的第一邻接点开始进行递归遍历,直到遇到遍历过的顶点,则返回上一层的下一个邻接点继续遍历,并打印顶点信息。所有顶点遍历完毕则返回 OK;
-
BFSTraverse:新建一个队列 q;把邻接表 G 内每个头结点的 book 值置零(用于判断是否遍历过该顶点);从位序为 0 的头结点开始,把它作为弧尾的弧所指向的表结点信息按顺序 push 进入队列;打印队头结点信息并对该结点执行同上的操作;反复执行直到队列为空;所有顶点遍历完毕则返回 OK;
3.2 源代码
(源代码详细见附录 D)
4. 系统测试
编程环境:Win10
编译器:Visual Studio 2019
主模块运行如图 4-1 所示:
图 4-1 主模块图
功能操作:
- CreateGraph:创建图;
- 输入“100”和“95”,创建一个位序为 0,名称为 100,携带信息为 95 的头结点;(如图 4-2 所示)
- 在未创建图的情况下选择任一选项都会返回“图不存在!”;(如图 4-3 所示)
图 4-2 创建头结点 1
图 4-3 图不存在反馈
- DestroyGraph:销毁图;
- 返回销毁成功;(如图 4-4 所示)
- 图不存在,则返回操作失败;
图 4-4 二叉树销毁成功
www.biyezuopin.vip
- LocateVex:查找顶点;
- 调用后面的函数创建一个图;(图的形式如图 4-5 所示)
- 对图进行深度遍历和广度遍历;(如图 4-6,4-7 所示)
- 输入 102,查找成功,获取返回值;(如图 4-8 所示)
- 输入 200,查找失败;(如图 4-9 所示)
- 图不存在,则返回操作失败;
图 4-5 创建图
图 4-6 深度遍历图
图 4-7 广度遍历图
图 4-8 查找成功
图 4-9 查找失败
- PutVex:为顶点赋值;
- 使用(3)中第一步建立的图;
- 分别为 100,101,102,103,104 赋值 96,97,98,99,100;(如图 4-10 所示)
- 对图进行深度遍历;(如图 4-11 所示)
- 输入为 201 赋值,则返回找不到该顶点;
- 图不存在,则返回操作失败;
图 4-10 赋值成功
图 4-11 深度遍历图
- FirstAdjVex:获得第一邻接点;
- 使用(4)中已被赋值的图;
- 输入 0,返回位序为 0 的 100 的第一邻接点 101 的位序 1;(如图 4-12 所示)
- 输入 6,返回输入不符合要求;(如图 4-13 所示)
- 图不存在,则返回操作失败;
图 4-12 返回第一邻接点
图 4-13 输入不符合要求
-
NextAdjVex:获得下一邻接点;
- 使用(4)中已被赋值的图;
- 输入 0 和 1,返回下一邻接点 102 的位序 2;(如图 4-14 所示)
- 输入 6 和 7,返回输入有误,查找失败;
- 输入 3 和 4,不存在下一邻接点,查找失败;
- 图不存在,则返回操作失败;
图 4-14 查找成功
- InsertVex:插入顶点;
- 使用(4)中已被赋值的图;
- 插入顶点 105,60,插入成功;(如图 4-15 所示)
- 插入顶点 101,10,该关键字已存在,插入失败;(如图 4-16 所示)
- 图不存在,则返回操作失败;
图 4-15 插入成功
图 4-16 插入失败
- DeleteVex:删除顶点;
- 使用(7)中插入新顶点后的图;
- 删除 104,返回删除成功(如图 4-17 所示);
- 此时图的形状;(如图 4-18 所示)
- 删除 200,顶点不存在,返回删除失败;
- 图不存在,则返回操作失败;
图 4-17 删除成功
图 4-18 此时的图
- InsertArc:插入弧;
- 使用(7)中删除完成的图;
- 插入(103,105)弧,进行深度遍历;(如图 4-19,4-20 所示)
- 插入(101,103)弧,返回该弧已存在;
- 插入(100,200)弧,返回插入失败;
- 图不存在,则返回操作失误;
图 4-19 插入弧成功
图 4-20 深度遍历
- DeleteArc:删除弧;
- 使用(9)中插入弧完成的图;
- 删除(102,101),删除成功;(如图 4-21 所示)
- 删除(105,101),返回该弧不存在,删除失败;
- 图不存在,则返回操作失误;
图 4-21 删除弧成功
- DFSTraverse:深度优先搜索遍历;
- 使用(10)中删除完成的图,进行深度优先搜索遍历;(图的形态如图 4-22 所示,遍历如图 4-23 所示)
- 图不存在,则返回操作失误;
图 4-22 图的形态
图 4-23 深度遍历
- BFSTraverse:广度优先搜索遍历;
- 使用(10)中删除完成的图,进行广度优先搜索遍历;(如图 4-24 所示)
- 图不存在,则返回操作失误;
图 4-24 广度遍历
5. 实验小结
本次 “基于邻接表的图实现”实验也是数据结构的最后一个实验,也是一次综合性的实验,在邻接表的构建中,用到了第一次顺序表实验、第二次链表实验的思想,同时写完二叉树实验的经历也让我编写遍历函数时的能力有所提升。当然,在本次实验中也出现了一些问题,收获了一些新经验:
-
在写添加新弧时,一开始没有加上检测(v,w)弧是否存在的判断语句,导致检查时候闹了乌龙。所以以后对边界条件的判断要更加小心谨慎,尽量考虑好每一种输入可能带来的结果;
-
在写深度优先搜索遍历和广度优先搜索遍历的时候,一开始没有思路,于是去网站上搜寻别人的代码,参照之前写二叉树遍历的经验,一行行地理解下来,在看懂后再自己动手按照思路写了一遍,然后多次运行修正。虽然花费了一些时间,但我认为这是最有效且掌握最佳的学习方法,帮助我更好地理解了深度遍历和广度遍历函数的理论思想和构建方法。
通过本次实验我学习到了很多,接下来的数据结构课设会是前所未有的难关,但我从这四次实验中学到的不只是函数的构建方法,更多的是高效的学习方法、细心谨慎的态度,以及完成实验的成就感所激发的学习的主观能动性。之后我也会不停止努力,充分吸收这四次实验给我带来的经验和启发,更好地完成之后的学习和实验。
附录 D 基于邻接表图实现的源程序
# include <stdio.h>
# include <malloc.h>
# include <stdlib.h>
# include <string.h>
# include <math.h>
# define TRUE 1
# define FALSE 0
# define OK 1
# define ERROR -1
# define INFEASIBLE -1
# define OVERFLOW -2
# define MAX_VERTEX_NUM 100
# define LIST_INIT_SIZE 100
# define NUM_MAX 30
typedef int Status;
typedef int ValueType;
typedef int KeyType;
typedef int InfoType;
static bool isNull = TRUE;
static int DeleteNum = 0;
FILE* graph;
typedef struct VertexType/*顶点存放的数据的结构类型*/
{
KeyType key; /*唯一标识*/
ValueType value;
} VertexType;
typedef struct ArcNode {
int adjvex; //该弧所指向的顶点的位置
struct ArcNode* nextarc; //指向下一条弧的指针
InfoType info; //该弧相关信息
} ArcNode;
typedef struct VNode {
VertexType data; //顶点信息
ArcNode* firstarc; //指向第一条依附该顶点的弧的指针
} VNode, AdjList[MAX_VERTEX_NUM];
typedef struct QueueList {
VNode data[LIST_INIT_SIZE];
int front;
int rear;
} queue, * Queue;
typedef struct ALGRAPH {
AdjList vertices;
int vexnum,arcnum; //图的当前节点数和弧数
int book[MAX_VERTEX_NUM] = { 0 };
} ALG, *ALGraph ;
/*Function defined*/
Status CreateGraph(ALGraph* G); /*创建图*/
Status DestroyGraph(ALGraph* G); /*销毁图*/
int LocateVex(ALGraph G,KeyType u); /*查找顶点*/
Status PutVex(ALGraph* G,KeyType u,ValueType v); /*顶点赋值*/
Status FirstAdjVex(ALGraph G, int index); /*获得第一邻接点*/
Status NextAdjVex(ALGraph G, int v, int w); /*获得下一邻接点*/
Status InsertVex(ALGraph* G, VertexType vnode); /*插入顶点*/
Status DeleteVex(ALGraph* G, KeyType vnode); /*删除顶点*/
Status InsertArc(ALGraph* G, KeyType v, KeyType w); /*插入弧*/
Status DeleteArc(ALGraph* G, KeyType v, KeyType w); /*删除弧*/
Status DFSTraverse(ALGraph* G); /*深度优先搜索遍历*/
void dfs(ALGraph* G, int i);
Status BFSTraverse(ALGraph* G); /*广度优先搜索遍历*/
Status Visit(VNode);
void InitQueue(Queue* q); /*新建队列*/
Status EnQueue(Queue* q, VNode T); /*入队*/
VNode OutQueue(Queue* q); /*出队*/
int main(void) {
int op = 1, index, vp, wp;
KeyType find, insert;
ValueType value;
ALGraph G = NULL;
VertexType* vnode;
char filename[NUM_MAX], loadname[NUM_MAX];
while (op) {
system("cls");
printf("\n\n-----------------Menu for Linear Table On Graph----------------\n");
printf(" 1. CreateGraph 7. InsertVex\n");
printf(" 2. DestroyGraph 8. DeleteVex\n");
printf(" 3. LocateVex 9. InsertArc\n");
printf(" 4. PutVex 10. DeleteArc \n");
printf(" 5. FirstAdjVex 11. DFSTraverse\n");
printf(" 6. NextAdjVex 12. BFSTraverse\n");
printf(" 0. Exit\n\n");
printf("---------------------------------------------------------------------\n");
printf("选择你的操作:\n");
scanf_s("%d", &op);
getchar();
switch (op)
{
case 1:
if (CreateGraph(&G) == OK)
printf("创建成功!\n");
else
printf("创建失败!\n");
getchar();
break;
case 2:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
if (DestroyGraph(&G) == OK)
printf("销毁成功!\n");
else
printf("销毁失败!\n");
getchar();
break;
case 3:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想查找的顶点的关键字:");
scanf_s("%d", &find);
getchar();
index = LocateVex(G, find);
if (index != ERROR) {
printf("顶点信息为: ");
Visit(G->vertices[index]);
printf("顶点在邻接表内第%d位.\n",index);
}
else {
printf("查找失败!");
}
getchar();
break;
case 4:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想赋值的顶点的关键字:");
scanf_s("%d", &find);
getchar();
index = LocateVex(G, find);
if (index != ERROR) {
printf("请输入你想赋的值:");
scanf_s("%d", &value);
getchar();
}
if (PutVex(&G, find, value) == OK)
printf("赋值成功!\n");
else
printf("赋值失败!\n");
getchar();
break;
case 5:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想查找的顶点的位序:");
scanf_s("%d", &index);
getchar();
if (index > G->vexnum + DeleteNum || index < 0) {
printf("输入位序不符合要求!\n");
getchar();
break;
}
if (FirstAdjVex(G, index) != ERROR)
printf("该顶点的第一个临接顶点为: %d!\n", FirstAdjVex(G, index));
else
printf("查找失败!\n");
getchar();
break;
case 6:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想查找的顶点v的位序:");
scanf_s("%d", &vp);
getchar();
printf("请输入你想查找的顶点w的位序:");
scanf_s("%d", &wp);
getchar();
if (vp == wp || vp > G->vexnum + DeleteNum || wp > G->vexnum + DeleteNum) {
printf("输入有误,查找失败。\n");
}
else if (NextAdjVex(G,vp,wp) != ERROR) {
printf("v相对于w的下一个临界顶点的位置为; %d!\n", NextAdjVex(G, vp, wp));
}
else
printf("查找失败!\n");
getchar();
break;
case 7:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想插入的顶点的关键字:");
scanf_s("%d", &insert);
getchar();
printf("请输入你想插入的顶点的值:");
scanf_s("%d", &value);
getchar();
vnode = (VertexType*)malloc(sizeof(VertexType));
if (!vnode) exit(OVERFLOW);
vnode->key = insert;
vnode->value = value;
if (InsertVex(&G, *vnode) == OK)
printf("插入成功!\n");
else
printf("插入失败!\n");
getchar();
break;
case 8:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想删除的顶点的关键字:");
scanf_s("%d", &find);
getchar();
if (DeleteVex(&G, find) == OK)
printf("删除成功!\n");
else
printf("删除失败!\n");
getchar();
break;
case 9:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想插入的弧尾的关键字:");
scanf_s("%d", &vp);
getchar();
printf("请输入你想插入的弧头的关键字:");
scanf_s("%d", &wp);
getchar();
if (InsertArc(&G, vp, wp) == OK)
printf("插入成功!\n");
else
printf("插入失败!\n");
getchar();
break;
case 10:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("请输入你想删除的弧尾的关键字:");
scanf_s("%d", &vp);
getchar();
printf("请输入你想删除的弧头的关键字:");
scanf_s("%d", &wp);
getchar();
if (DeleteArc(&G,vp,wp) == OK)
printf("删除成功!\n");
else
printf("删除失败!\n");
getchar();
break;
case 11:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("对图进行深度优先搜索遍历如下:\n");
DFSTraverse(&G);
getchar();
break;
case 12:
if (isNull == TRUE) {
printf("图不存在!\n");
getchar();
break;
}
printf("对图进行广度优先搜索遍历如下:\n");
BFSTraverse(&G);
getchar();
break;
case 0:
break;
}
}
printf("感谢使用本系统!\n");
return 0;
}
/*case1: 创建图*/
Status CreateGraph(ALGraph* G) {
KeyType key;
ValueType value;
VNode* vnode = (VNode*)malloc(sizeof(VNode));
isNull = FALSE;
*G = (ALGraph)malloc(sizeof(ALG));
if (!G) exit(OVERFLOW);
(*G)->vexnum = 0;
(*G)->arcnum = 0;
printf("请输入顶点的key值(唯一):");
scanf_s("%d", &key);
getchar();
printf("请为该顶点赋值:");
scanf_s("%d", &value);
getchar();
if (key) {
vnode->data.key = key;
vnode->data.value = value;
vnode->firstarc = NULL;
}
(*G)->vertices[(*G)->vexnum] = *vnode;
(*G)->vexnum++;
return OK;
}
/*case2: 销毁图*/
Status DestroyGraph(ALGraph* G) {
if (!G) return ERROR;
isNull = TRUE;
VNode* vnode;
ArcNode* del,*temp=NULL;
for (int i = 0; i < (*G)->vexnum + DeleteNum; i++) {
del = (*G)->vertices[i].firstarc;
while (del != NULL) {
temp = del->nextarc;
free(del);
del = temp;
}
}
*G = NULL;
return OK;
}
/*case3: 查找顶点*/
int LocateVex(ALGraph G, KeyType u) {
for (int i = 0; i < (*G).vexnum + DeleteNum; i++) {
if ((*G).vertices[i].data.key == u) {
return i;
}
}
return ERROR;
}
/*case4: 顶点赋值*/
Status PutVex(ALGraph* G, KeyType u, ValueType v) {
if (!G) return ERROR;
for (int i = 0; i < (*G)->vexnum + DeleteNum; i++) {
if ((*G)->vertices[i].data.key == u) {
(*G)->vertices[i].data.value = v;
return OK;
}
}
return ERROR;
}
/*case5:获得第一邻接点*/
Status FirstAdjVex(ALGraph G, int index) {
ArcNode* arc = NULL;
if (index < (*G).vexnum + DeleteNum) {
if ((*G).vertices[index].firstarc) {
arc = (*G).vertices[index].firstarc;
return arc->adjvex;
}
}
return ERROR;
}
/*case6: 获得下一邻接点*/
Status NextAdjVex(ALGraph G, int v, int w) {
ArcNode* arc = NULL;
if (v == w) return ERROR;
if (v < (*G).vexnum + DeleteNum && w < (*G).vexnum + DeleteNum) {
arc = (*G).vertices[v].firstarc;
while (arc) {
if (arc->adjvex == w && arc->nextarc)
return arc->nextarc->adjvex;
arc = arc->nextarc;
}
}
return ERROR;
}
/*case7: 插入顶点*/
Status InsertVex(ALGraph* G, VertexType vnode) {
if (!G) return ERROR;
if (LocateVex(*G, vnode.key) != ERROR) {
printf("该关键字已存在!");
return ERROR;
}
(*G)->vertices[(*G)->vexnum].data = vnode;
(*G)->vertices[(*G)->vexnum].firstarc = NULL;
(*G)->vexnum++;
return OK;
}
/*case8: 删除顶点*/
Status DeleteVex(ALGraph* G, KeyType vnode) {
if (!G) return ERROR;
ArcNode* temp;
int num = 0;
if (LocateVex(*G, vnode) == ERROR) {
printf("找不到该顶点!");
return ERROR;
}
for (int i = 0; i < (*G)->vexnum + DeleteNum; i++) {
temp = (*G)->vertices[i].firstarc;
if (!temp) continue;
if ((*G)->vertices[i].data.key == vnode) {//删顶点
(*G)->vertices[i].data.key = 0;
(*G)->vexnum--;
while (temp) {
num++;
temp = temp->nextarc;
}
(*G)->vertices[i].firstarc = NULL;
(*G)->arcnum = (*G)->arcnum - num;
num = 0;
}
else { //删除弧
if ( temp->adjvex == LocateVex(*G,vnode)) {
(*G)->vertices[i].firstarc = temp->nextarc;
(*G)->arcnum--;
}
while (temp->nextarc) {
if (temp->nextarc->adjvex == LocateVex(*G, vnode)) {
temp->nextarc = temp->nextarc->nextarc;
(*G)->arcnum--;
}
temp = temp->nextarc;
}
}
}
DeleteNum++;
return OK;
}
/*case 9: 插入弧*/
Status InsertArc(ALGraph* G, KeyType v, KeyType w) {
if (!G) return ERROR;
int insert;
ArcNode* temp,*p;
if (LocateVex(*G, v)!=ERROR && LocateVex(*G, w)!=ERROR) {
insert = LocateVex(*G, v);
temp = (*G)->vertices[insert].firstarc;
if (!temp) {
(*G)->vertices[insert].firstarc = (ArcNode*)malloc(sizeof(ArcNode));
(*G)->vertices[insert].firstarc->adjvex = LocateVex(*G, w);
(*G)->vertices[insert].firstarc->info = 0;
(*G)->vertices[insert].firstarc->nextarc = NULL;
(*G)->arcnum++;
return OK;
}
while (temp->nextarc) {
if ((*G)->vertices[temp->adjvex].data.key == w) {
printf("该弧已存在。 ");
return ERROR;
}
temp = temp->nextarc;
}
if ((*G)->vertices[temp->adjvex].data.key == w) {
printf("该弧已存在。 ");
return ERROR;
}
p = temp;
temp = temp->nextarc;
temp = (ArcNode*)malloc(sizeof(ArcNode));
temp->adjvex = LocateVex(*G, w);
temp->info = 0;
temp->nextarc = NULL;
p->nextarc = temp;
(*G)->arcnum++;
return OK;
}
return ERROR;
}
/*case 10: 删除弧*/
Status DeleteArc(ALGraph* G, KeyType v, KeyType w) {
if (!G) return ERROR;
ArcNode* temp;
int indexv,indexw;
if (LocateVex(*G, v)!=ERROR && LocateVex(*G, w)!=ERROR) {
indexv = LocateVex(*G, v);
indexw = LocateVex(*G, w);
temp = (*G)->vertices[indexv].firstarc;
if (!temp) {
printf("该顶点不是弧尾!");
return ERROR;
}
if (temp && temp->adjvex == indexw) {
(*G)->vertices[indexv].firstarc = temp->nextarc;
(*G)->arcnum--;
return OK;
}
while (temp->nextarc) {
if (temp->nextarc->adjvex == indexw) {
temp->nextarc = temp->nextarc->nextarc;
(*G)->arcnum--;
return OK;
}
temp = temp->nextarc;
}
printf("不存在从v指向w的弧!");
}
return ERROR;
}
/*case 11: 深度优先搜索遍历*/
Status DFSTraverse(ALGraph* G) {
if (!G) return ERROR;
int i = 0;
for (int j = 0; j < MAX_VERTEX_NUM; j++) (*G)->book[j] = 0 ;
for (i = 0; i < (*G)->vexnum + DeleteNum; i++) {
if (!(*G)->book[i])
dfs(G, i);
}
return OK;
}
void dfs(ALGraph* G, int i) {
ArcNode* a;
(*G)->book[i] = 1;
Visit((*G)->vertices[i]);
a = (*G)->vertices[i].firstarc;
while (a) {
if ((*G)->book[a->adjvex] == 0)
{
dfs(G, a->adjvex);
}
a = a->nextarc;
}
}
/*case 12: 广度优先搜索遍历*/
Status BFSTraverse(ALGraph* G) {
if (!G) return ERROR;
Queue q;
ArcNode* arc;
int i = 0;
VNode v = (*G)->vertices[0];
for (int j = 0; j < MAX_VERTEX_NUM; j++) (*G)->book[j] = 0;
InitQueue(&q);
EnQueue(&q, v);
while (q->front != q->rear) {
v = OutQueue(&q);
if ((*G)->book[LocateVex(*G, v.data.key)] == 0) {
Visit(v);
(*G)->book[LocateVex(*G, v.data.key)] = 1;
}
arc = v.firstarc;
while (arc) {
if ((*G)->book[arc->adjvex] == 0) {
Visit((*G)->vertices[arc->adjvex]);
(*G)->book[arc->adjvex] = 1;
EnQueue(&q, (*G)->vertices[arc->adjvex]);
}
arc = arc->nextarc;
}
}
for (i = 0; i < (*G)->vexnum + DeleteNum; i++) {
if (!(*G)->book[i])
Visit((*G)->vertices[i]);
}
return OK;
}
void InitQueue(Queue* q) {
*q = (Queue)malloc(sizeof(queue));
if ((*q) == NULL) exit(OVERFLOW);
(*q)->front = 0;
(*q)->rear = 0;
}
Status EnQueue(Queue* lq, VNode ch) { /*入队*/
if ((*lq)->rear >= LIST_INIT_SIZE)
return ERROR;
(*lq)->data[(*lq)->rear] = ch;
(*lq)->rear++;
return OK;
}
VNode OutQueue(Queue* q) { /*出队*/
VNode tempnode = (*q)->data[(*q)->front];
(*q)->front++;
return tempnode;
}
Status Visit(VNode v) {
if ( v.data.key == 0) return ERROR;
printf("%d: %d\n", v.data.key, v.data.value);
return OK;
}
(*G)->book[arc->adjvex] = 1;
EnQueue(&q, (*G)->vertices[arc->adjvex]);
}
arc = arc->nextarc;
}
}
for (i = 0; i < (*G)->vexnum + DeleteNum; i++) {
if (!(*G)->book[i])
Visit((*G)->vertices[i]);
}
return OK;
}
void InitQueue(Queue* q) {
*q = (Queue)malloc(sizeof(queue));
if ((*q) == NULL) exit(OVERFLOW);
(*q)->front = 0;
(*q)->rear = 0;
}
Status EnQueue(Queue* lq, VNode ch) { /入队/
if ((*lq)->rear >= LIST_INIT_SIZE)
return ERROR;
(*lq)->data[(*lq)->rear] = ch;
(*lq)->rear++;
return OK;
}
VNode OutQueue(Queue* q) { /出队/
VNode tempnode = (*q)->data[(*q)->front];
(*q)->front++;
return tempnode;
}
Status Visit(VNode v) {
if ( v.data.key == 0) return ERROR;
printf(“%d: %d\n”, v.data.key, v.data.value);
return OK;
}