数据结构 实验报告(四)图的遍历算法实现

前言

学校的作业,如果看到了,不用怀疑,就是校友😀

实验说明

数据结构实验四 图的实验——图的主要遍历算法实现

一、实验目的

通过本实验使学生熟悉图遍历的两种方法:深度优先与广度优先;掌握编程实现图遍历具体算法;深刻理解图的顺序存储(邻接矩阵)与链式存储(邻接链表)的特性;特别训练学生在编程上控制复杂结构的能力,为今后控制更为复杂结构,进而解决有一定难度的复杂问题奠定基础。

二、实验内容

1.分别采用邻接表实现图的深度优先与广度优先遍历算法。
2.采用邻接矩阵实现图的广度优先遍历和深度优先遍历算法。

实验报告

1.实现功能描述

采用邻接表实现图的深度优先与广度优先遍历算法。采用邻接矩阵实现图的广度优先遍历和深度优先遍历算法。

2.方案比较与选择

(1)可以使用链表和队列来实现。因为队列的功能较全且更符合题目要求,所以使用队列来实现。

3.设计算法描述

(1)定义一个结构体代表结点,其中包含数据域data和指向第一条依附于该结点的弧指针。
(2)设计队列。
(3)进行模块划分,给出功能组成框图。形式如下:

(4)基本功能模块:
①创建无向图
②深度优先遍历无向图
③广度优先遍历无向图
(5)用流程图描述关键算法:

4.算法实现(即完整源程序,带注解)

(1)邻接表:

点击查看详细内容
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h> 
#define MAX_VERTEX_NUM 20	//最大结点个数 
typedef char VertexType;
typedef int VRType;
typedef int InfoType;	//图中边上的权值信息 
typedef int QElemType;	//队列中结点数据类型 

/* 图的深度优先遍历和广度优先遍历 */
//邻接表存储图
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 {
	AdjList vertices;
	int vexnum, arcnum;	//结点数和弧树 
}ALGraph;

//创建用于广度优先遍历的队列
typedef struct QNode {
	QElemType data;
	struct QNode* qnext;
}QNode, * PQNode;

typedef struct Queue {
	PQNode front;
	PQNode rear;
}Queue, * PQueue;

bool visited[MAX_VERTEX_NUM];  //标记结点是否被遍历过,否为flase,是为true;

PQueue initQueue();	//初始化一个空队列 
void enQueue(PQueue pqueue, QElemType data);	//队尾入队
bool isEmpty(PQueue pqueue);	//判断队列是否为空
QElemType deQueue(PQueue pqueue);	//队头出队 
int locateVex(ALGraph alg, char v);	//确定图中结点位置编号
void createALGraph(ALGraph* alg);	//创建无向图 
void DFS(ALGraph alg, int v);	//深度优先遍历无向图
void BFSTraverse(ALGraph alg);	//广度优先遍历
void DFSTraverse(ALGraph alg);	//对邻接表存储的无向图进行深度优先遍历

/*
测试用例
8 10
1
2
3
4
5
6
7
8
1 2
1 3
2 4
2 5
3 6
3 7
4 8
5 8
6 8
7 8
测试结果
1 2 4 8 5 6 3 7
1 2 3 4 5 6 7 8
*/

int main() {
	ALGraph alg;
	createALGraph(&alg);   //创建无向图 
	DFSTraverse(alg);
	printf("\n");
	BFSTraverse(alg);
	printf("\n");
	return 0;
}

PQueue initQueue() {
	PQueue pqueue = (PQueue)malloc(sizeof(Queue));
	PQNode pqnode = (PQNode)malloc(sizeof(QNode));
	if (pqnode == NULL) {
		printf("队列头空间申请失败!\n");
		exit(-1);
	}
	pqueue->front = pqueue->rear = pqnode;
	pqnode->qnext = NULL;
	return pqueue;
}

void enQueue(PQueue pqueue, QElemType data) {
	PQNode pqnode = (PQNode)malloc(sizeof(QNode));
	if (pqnode == NULL) {
		printf("队列结点申请失败!\n");
		exit(-1);
	}
	pqnode->data = data;
	pqnode->qnext = NULL;
	pqueue->rear->qnext = pqnode;
	pqueue->rear = pqnode;
}

bool isEmpty(PQueue pqueue) {
	if (pqueue->front == pqueue->rear) return true;
	return false;
}

QElemType deQueue(PQueue pqueue) {
	if (isEmpty(pqueue)) {
		printf("队列为空\n");
		exit(-1);
	}
	PQNode pqnode = pqueue->front->qnext;
	pqueue->front->qnext = pqnode->qnext;
	if (pqnode == pqueue->rear) pqueue->rear = pqueue->front;
	QElemType data = pqnode->data;
	free(pqnode);
	return data;
}

int locateVex(ALGraph alg, char v) {
	int i;
	for (i = 0; i < alg.vexnum; i++) {
		if (alg.vertices[i].data == v) return i;
	}
	return -1;
}

void createALGraph(ALGraph* alg) {
	int i, j, v, k;
	printf("请输入所创建无向图的结点数和边数(用空格隔开):");
	scanf("%d %d", &(*alg).vexnum, &(*alg).arcnum);
	getchar();
	for (i = 0; i < (*alg).vexnum; i++) {
		printf("输入第%d个结点名称:", i+1);
		scanf("%c", &(*alg).vertices[i].data);
		(*alg).vertices[i].firstarc = NULL;
		getchar();
	}
	char v1, v2;
	ArcNode* s, * p;
	for (k = 0; k < (*alg).arcnum; k++) {
		printf("输入第%d条边的两个结点名称:", k+1);
		scanf("%c %c", &v1, &v2);
		i = locateVex((*alg), v1);
		j = locateVex((*alg), v2);
		//由于是无向图因此一条边需要关联两个结点 
		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex = j;
		p->nextarc = NULL;
		if ((*alg).vertices[i].firstarc == NULL) {
			(*alg).vertices[i].firstarc = p;
		}
		else {
			s = (*alg).vertices[i].firstarc;
			while (s->nextarc != NULL)
				s = s->nextarc;
			s->nextarc = p;
		}

		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex = i;
		p->nextarc = NULL;
		if ((*alg).vertices[j].firstarc == NULL) (*alg).vertices[j].firstarc = p;
		else {
			s = (*alg).vertices[j].firstarc;
			while (s->nextarc != NULL)
				s = s->nextarc;
			s->nextarc = p;
		}
		getchar();
	}
}

void DFS(ALGraph alg, int v) {
	//从第v个结点出发递归的深度优先遍历图alg
	ArcNode* p;
	visited[v] = true;
	printf("%c ", alg.vertices[v].data);
	for (p = alg.vertices[v].firstarc; p != NULL; p = p->nextarc) {
		if (!visited[p->adjvex])
			DFS(alg, p->adjvex);
	}
}

void DFSTraverse(ALGraph alg) {
	printf("深度优先遍历序列:");
	int v;
	for (v = 0; v < alg.vexnum; v++)
		visited[v] = false;
	for (v = 0; v < alg.vexnum; v++) {
		if (!visited[v])
			DFS(alg, v);
	}
}

void BFSTraverse(ALGraph alg) {
	printf("广度优先遍历序列:");
	PQueue pqueue = initQueue();
	ArcNode* p;
	int i;
	QElemType v;
	for (i = 0; i < alg.vexnum; i++)
		visited[i] = false;
	for (i = 0; i < alg.vexnum; i++) {
		if (!visited[i]) {
			visited[i] = true;
			printf("%c ", alg.vertices[i].data);
			enQueue(pqueue, i);
			while (!isEmpty(pqueue)) {
				v = deQueue(pqueue);
				for (p = alg.vertices[v].firstarc; p != NULL; p = p->nextarc) {
					if (!visited[p->adjvex]) {
						printf("%c ", alg.vertices[p->adjvex].data);
						visited[p->adjvex] = true;
						enQueue(pqueue, p->adjvex);
					}
				}
			}
		}
	}
}

(2)邻接矩阵:

点击查看详细内容
#include <stdio.h>
#include <string.h>
#include <windows.h>
#define MaxVertexNum  100   //结点数目最大值
#define maxSize 20			//队列最大值
typedef char VertexType;	//结点的数据类型
typedef int EdgeType;		//带权图中边上权值的数据类型

//队列
typedef struct
{
	int data[maxSize];
	int front, rear;
}Queue;

typedef struct
{
	VertexType Vex[MaxVertexNum];   //结点表
	EdgeType Edge[MaxVertexNum][MaxVertexNum];  //邻接矩阵,边表
	int vexnum, edgenum;    //图的结点数和弧数
}MGraph;

int visitDFS[maxSize];
int visitBFS[maxSize];
void create_Graph(MGraph* G);	//创建无向图
void InitQueue(Queue* Q);	//初始化队列
int IsEmpty(Queue* Q);	//判断队空
void EnQueue(Queue* Q, int e);	//入队
void DeQueue(Queue* Q, int* e);	//出队
void DFS(MGraph G, int i);	//深度优先遍历
void DFSTraverse(MGraph G);	//深度优先遍历
void BFS(MGraph G);	//广度优先遍历

/*
测试用例
8 10
1
2
3
4
5
6
7
8
1 2 1
1 3 1
2 4 1
2 5 1
3 6 1
3 7 1
4 8 1
5 8 1
6 8 1
7 8 1
测试结果
1 2 4 8 5 6 3 7
1 2 3 4 5 6 7 8
*/

void main(){
	MGraph G;
	create_Graph(&G);
	DFSTraverse(G);
	BFS(G);
	printf("\n");
}

void create_Graph(MGraph* G) {
	int i, j;
	int start, end;  //边的起点序号、终点序号
	int numV, numE;
	int w;   //边上的权值
	printf("请输入所创建无向图的结点数和边数(用空格隔开):");
	scanf_s("%d%d", &numV, &numE);
	G->vexnum = numV;
	G->edgenum = numE;
	//图的初始化
	for (i = 0; i < G->vexnum; i++) {
		for (j = 0; j < G->vexnum; j++) {
			if (i == j) G->Edge[i][j] = 0;
			else G->Edge[i][j] = 32767;
		}
	}

	//结点信息存入结点表
	for (i = 0; i < G->vexnum; i++) {
		printf("输入第%d个结点名称:", i + 1);
		scanf_s("%d", &G->Vex[i]);
	}
	printf("\n");
	//输入无向图边的信息
	for (i = 0; i < G->edgenum; i++) {
		printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
		scanf_s("%d%d%d", &start, &end, &w);
		G->Edge[start - 1][end - 1] = w;
		G->Edge[end - 1][start - 1] = w;   //无向图具有对称性
	}
}

void InitQueue(Queue* Q) {
	Q->front = Q->rear = 0;
}

int IsEmpty(Queue* Q) {
	if (Q->front == Q->rear) return 1;
	else return 0;
}

void EnQueue(Queue* Q, int e) {
	if ((Q->rear + 1) % maxSize == Q->front) return;
	else {
		Q->data[Q->rear] = e;
		Q->rear = (Q->rear + 1) % maxSize;
	}
}

void DeQueue(Queue* Q, int* e) {
	if (Q->rear == Q->front) return;
	*e = Q->data[Q->front];
	Q->front = (Q->front + 1) % maxSize;
}

void DFS(MGraph G, int i) {
	int j;
	visitDFS[i] = 1;
	printf("%d ", G.Vex[i]);
	for (j = 0; j < G.vexnum; j++) {
		if (G.Edge[i][j] != 32767 && !visitDFS[j]) DFS(G, j);
	}
}

void DFSTraverse(MGraph G) {
	int i;
	printf("\n深度优先遍历序列:");
	for (i = 0; i < G.vexnum; i++) visitDFS[i] = 0;
	for (i = 0; i < G.vexnum; i++) {
		if (!visitDFS[i]) DFS(G, i);
	}
}

void BFS(MGraph G) {
	int i, j;
	Queue Q;
	printf("\n广度优先遍历序列:");
	for (i = 0; i < G.vexnum; i++) visitBFS[maxSize] = 0;
	InitQueue(&Q);
	for (i = 0; i < G.vexnum; i++) {
		if (!visitBFS[i]) {
			visitBFS[i] = 1;
			printf("%d ", G.Vex[i]);
			EnQueue(&Q, i);

			while (!IsEmpty(&Q)) {
				DeQueue(&Q, &i);
				for (j = 0; j < G.vexnum; j++) {
					if (!visitBFS[j] && G.Edge[i][j] != 32767) {
						visitBFS[j] = 1;
						printf("%d ", G.Vex[j]);
						EnQueue(&Q, j);
					}
				}
			}
		}
	}
}

5.实验结果测试与分析

(1)数据测试程序截图


(2)对结果进行分析:
①邻接表:深度优先遍历正确
②邻接表:广度优先遍历正确
③邻接矩阵:深度优先遍历正确
④邻接矩阵:深度优先遍历正确
⑤队列运行正常

6.思考及学习心得

(1)描述实验过程中对此部分知识的认识:
(2)特别描述在学习方法上的收获及体会;
(3)针对前面的思考题内容在此回答。
1)实现了队列的功能,更进一步理解和掌握队列的使用。

2)这次的实验,巩固了我的编程模块化的思想。模块化降低了程序的耦合性,提高了程序的内聚性;降低了程序复杂度,使程序设计、调试和维护等操作简单化。模块化使得程序设计更加简单和直观,从而提高了程序的易读性和可维护性,而且还可以把程序中经常用到的一些计算或操作编写成通用函数,以供随时调用。

3)对于顺序存储结构和链式存储的遍历算法,在时空效率上与进行分析对比,并得出结论:
链表法时间复杂度较高,空间复杂度较低;数组法时间复杂度较低,空间复杂度较高。因为数组法一开始就定义好树的大小,如果有空节点就浪费了空间,而链表法不会创建空结点,因此数组法的空间复杂度较高。链表法对指针的操作较繁琐,所需时间长,因此链表法的时间复杂度较低。

posted @ 2020-10-28 14:55  吃猫的鱼℘  阅读(9956)  评论(0编辑  收藏  举报
Document