一、 实验目的
树和图是两种应用极为广泛的数据结构,也是这门课程的重点。它们的特点在于非线性。 稀疏矩阵的十字链表存储结构也是图的一种存储结构。本章实验继续突出了数据结构加操作的程序设计观点,但根据这种结构的非线性特点,将操作进一步集中在遍历操作上,因为遍历操作是其它众多操作的基础。遍历逻辑的(或符号形式的)结构,访问动作可是任何操作。本次实验希望帮助学生熟悉各种存储结构的特征,以及如何应用图结构解决具体问题(即原理与应用的结合)。
二、 实验内容
1) 图遍历的演示
[问题描述]
很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示无向图的遍历操作。
[基本要求]
以邻接表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
[测试数据]
由学生依据软件工程的测试技术自己确定。注意测试边界数据,如单个结点。
[实现提示]
设图的结点不超过30个,每个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,…,n)。通过输入图的全部边输入一个图,每条边为一个数对,可以对边的输入顺序做出某种限制。注意,生成树的边是有向边,端点顺序不能颠倒。
三、 实验环境
Visual Studio 2013
Windows 10
#include<stdio.h> #include<stdlib.h> int visited[20]; typedef struct ArcNode{ int adjvex; //邻接点域 struct ArcNode * nextarc; //指向下一个邻接点的指针域 }; typedef struct VNode{ //表头节点 int data; //顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 }VNode, AdjList[20]; typedef struct{ AdjList vertices; int vexnum, arcnum; }ALGraph; typedef struct{ int start; int end; }Arc; Arc BSet[20]; //广度优先搜索生成的边的边集 Arc DSet[20]; //深度优先搜索生成的边的边集 int b = 0; //广度优先搜索边集的index int d = 0; //深度优先搜索边集的index void createGraph(ALGraph &G){ printf("请输入图的顶点的个数。\n"); scanf_s("%d", &G.vexnum); printf("请输入图的边的个数。\n"); scanf_s("%d", &G.arcnum); printf("请输入顶点的信息。\n"); for (int i = 0; i<G.vexnum; i++){ scanf_s("%d", &((G.vertices[i]).data)); G.vertices[i].firstarc = NULL; } printf("请输入边的信息\n"); int a, b; for (int j = 0; j<G.arcnum; j++){ scanf_s("%d%d", &a, &b); ArcNode* s = (ArcNode*)malloc(sizeof(ArcNode)); s->adjvex = a; s->nextarc = G.vertices[b].firstarc; G.vertices[b].firstarc = s; s = (ArcNode*)malloc(sizeof(ArcNode)); s->adjvex = b; s->nextarc = G.vertices[a].firstarc; G.vertices[a].firstarc = s; } printf("邻接表创建完成。\n"); } //深度优先遍历 void DFS(ALGraph G, int i){ //以i为开始开始遍历 printf("%d ", i); ArcNode *p,*q; visited[i] = 1; //0为假,1为真 p = G.vertices[i].firstarc; while (p){ if (!visited[p->adjvex]){ DSet[d].start = i; DSet[d].end = p->adjvex; d++; DFS(G, p->adjvex); } p = p->nextarc; } } void DFSTraverse(ALGraph G){ int j, i; printf("请输入从哪个点开始进行深度优先搜索。\n"); scanf_s("%d", &j); printf("深度优先搜索遍历顺序为:"); // DSet[d] = j; for (i = 0; i<G.vexnum; i++){ visited[i] = 0; } for (i = j; i<G.vexnum; i++){ if (!visited[i]){ DFS(G, i); } } for (i = 0; i<j; i++){ if (!visited[i]){ DFS(G, i); } } printf("\n"); } void PrintDFTree(ALGraph G){ int i = 0; printf("深度生成树的边集为:"); for (; i < G.vexnum - 1; i++){ printf("(%d,%d)\t",DSet[i].start,DSet[i].end); } printf("\n"); } //广度优先遍历 typedef struct QNode{ int data; struct QNode* next; }QNode, *QuePtr; typedef struct { QuePtr front; QuePtr rear; }LinkQue; int Init(LinkQue &Q){ Q.front = Q.rear = (QuePtr)malloc(sizeof(QNode)); if (!Q.front) return -1; Q.front->next = NULL; //不要忘了把头结点的next手动定义为null // printf("success in initializating.\n"); return 0; } int enQue(LinkQue &Q, int e){ QNode *p = (QNode*)malloc(sizeof(QNode)); if (!p) return -1; p->data = e; p->next = NULL; //别忘了null Q.rear->next = p; Q.rear = p; return 0; } int deQue(LinkQue &Q, int &e){ if (Q.front == Q.rear){ printf("The queue is empty now.\n"); return -1; } QNode* p = Q.front->next; //Q.front 是一个没有存放任何东西的头结点 e = p->data; //printf("The data is %d\n",e); Q.front->next = p->next; if (Q.rear == p) Q.rear = Q.front; //队列中只有一个元素的情况 先把rear挪到前面来 要不然删的话把rear也一起删掉了 free(p); return 0; } void BFS(ALGraph G, int i){ printf("%d ", i); visited[i] = 1; LinkQue Q; int e; Init(Q); enQue(Q, i); while (Q.front != Q.rear){ deQue(Q, i); ArcNode *p = G.vertices[i].firstarc; while (p){ if (!visited[p->adjvex]){ printf("%d ", p->adjvex); visited[p->adjvex] = 1; enQue(Q, p->adjvex); BSet[b].start = i; BSet[b].end = p->adjvex; b++; } p = p->nextarc; } } } void BFSTraverse(ALGraph G){ int i, j; printf("请输入从哪个点开始进行广度优先搜索。\n"); scanf_s("%d", &j); printf("广度优先搜索遍历顺序为:\n"); for (i = 0; i<G.vexnum; i++){ visited[i] = 0; } for (i = j; i<G.vexnum; i++){ if (!visited[i]){ BFS(G, i); } } for (i = 0; i<j; i++){ if (!visited[i]){ BFS(G, i); } } printf("\n"); } void PrintBFTree(ALGraph G){ int i = 0; printf("广度生成树的边集为:\n"); for (; i < G.vexnum - 1; i++){ printf("(%d,%d)\t", BSet[i].start, BSet[i].end); } printf("\n"); } int main(){ ALGraph G; createGraph(G); DFSTraverse(G); PrintDFTree(G); BFSTraverse(G); PrintBFTree(G); getchar(); getchar(); return 0;
1. 程序总是在最后出现闪退,加了一个getchar()不行,继续加一个getchar()成功
2. 在最开始深度优先搜索的时候每次都不会输出第一个遍历的点,原因是在第一次进入DFS的时候,visited就已经被置为1,但是自己的输出错误的写在了if结构里面,这就导致了第一个遍历的点未被输出,最后将输出语句放在了一进入DFS的位置,运行成功。
3. 在保存生成树的边集的时候最初的想法是在进入DFS函数的时候依次保存起点,在结束DFS函数的时候依次保存终点,使用一个全局的index进行控制,但是运行出现内存访问冲突的错误,于是改变策略,起点为i,终点为p->advex。
4. 在最初的时候并不能够实现指定遍历的起点,于是在traverse函数里面使用两个并列有先后次序的for循环来实现。
5. 最初的时候审题错误,输入字母的节点,之后对应编号,出了一些小错误。其实按照题目要求应该是直接用编号来表示顶点。
运行结果
测试数据为 8 个节点,10条边。
顶点为 0,1,2,3,4,5,6,7
边为(0,1)(0,2)(1,3)(1,4)(2,5)(2,6)(3,7)(4,7)(5,7)(6,7)