zoukankan      html  css  js  c++  java
  • 数据结构 实验报告(四)图的遍历算法实现

    实验说明

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

    一、实验目的

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

    二、实验内容

    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("
    ");
    	BFSTraverse(alg);
    	printf("
    ");
    	return 0;
    }
    
    PQueue initQueue() {
    	PQueue pqueue = (PQueue)malloc(sizeof(Queue));
    	PQNode pqnode = (PQNode)malloc(sizeof(QNode));
    	if (pqnode == NULL) {
    		printf("队列头空间申请失败!
    ");
    		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("队列结点申请失败!
    ");
    		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("队列为空
    ");
    		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("
    ");
    }
    
    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("
    ");
    	//输入无向图边的信息
    	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("
    深度优先遍历序列:");
    	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("
    广度优先遍历序列:");
    	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)对于顺序存储结构和链式存储的遍历算法,在时空效率上与进行分析对比,并得出结论:
    链表法时间复杂度较高,空间复杂度较低;数组法时间复杂度较低,空间复杂度较高。因为数组法一开始就定义好树的大小,如果有空节点就浪费了空间,而链表法不会创建空结点,因此数组法的空间复杂度较高。链表法对指针的操作较繁琐,所需时间长,因此链表法的时间复杂度较低。

  • 相关阅读:
    自定义弹框
    微信分享
    RichText
    UIDatePicker
    微服务概述
    超详细十大经典排序算法总结
    《Java程序员面试笔试宝典》学习笔记(持续更新……)
    知识图谱让分析工作化繁就简
    构建以知识图谱为核心的下一代数据中台
    智慧安监系统为城市安全监管提供保障
  • 原文地址:https://www.cnblogs.com/ast935478677/p/13891126.html
Copyright © 2011-2022 走看看