实验 3:图形数据结构的实现与应用

news2024/12/28 20:03:04
  • 东莞理工学院的同学可以借鉴,请勿抄袭

1.实验目的

通过实验达到:

  1. 理解和掌握图的基本概念、基本逻辑结构;

  2. 理解和掌握图的邻接矩阵存储结构、邻接链表存储结构;

  3. 理解和掌握图的 DFS、BFS 遍历操作的思想及其实现;

  4. 加深对堆栈、队列的概念及其典型操作思想的理解;

  5. 理解和掌握图的应用-最小生成树、最短路径算法的思想及其实现;

  6. 掌握典型图操作算法的算法分析。

2. 实验题目:图的建立、遍历及其应用

设图结点的元素类型为 ElemType(可以为 char 或 int),通过文件读取方式, 建立一个不少于10个顶点的带权无向图G,实现以下图的各种基本操作的程序:

① 用邻接矩阵作为储结构存储图 G 并输出该邻接矩阵;

② 用邻接链表作为储结构存储图 G 并输出该邻接链表;

③ 按深度优先遍历(DFS)算法输出图 G 中顶点的遍历序列;

④ 按广度优先遍历(BFS)算法输出图 G 中顶点的遍历序列;

⑤ 使用 Prime 算法(或者 Kruskal 算法)从某个指定的顶点出发输出图 G 的 最小生成树;(要求把最小生成树的各条边输出成 A-B-wight,或者 (A,B,weight)的形式);

⑥ 求从有向图的某个节点出发到其余各顶点的最短路径和最短路径值; (带权有向图);

⑦ 主函数通过菜单选择函数调用实现以上各项操作,请在实验报告中请画 出设计的图。

附加题:(每完成一个额外附加 5 分,上限 10 分)

① 编写函数求邻接矩阵存储结构的有向图 G 中各顶点的入度和出度;

② 用狄克斯特拉(Dijkastra)算法或者 Floyd 算法求每对顶点之间的最 短路径;(带权有向图)。

一个无向图的例子,可以把此图扩展作为实验用图:

在这里插入图片描述

2.1. 数据结构设计

typedef char ElemType;

typedef struct GraphByMatrix {
	//顶点数组
	ElemType* arrayV;

	//邻接矩阵
	int** matrix;

	//有向or无向
	int isDirect;

	int size;

}GraphByMatrix;

typedef struct Node {
	int src;
	int dest;
	int weight;
	struct Node* next;
}Node;

typedef struct GraphByList {
	Node** edges;
	ElemType* arrayV;
	int isDirect;
	int size;//顶点个数
}GraphByList;

2.2. 主要操作算法设计与分析

2.2.1. 读文件建立邻接矩阵并输出

GraphByMatrix* createMByFile(char* file);

GraphByMatrix* createGByM(ElemType* arrayV, int n, int flag);

int getIndexM(GraphByMatrix* pg, ElemType v);

void addEdgeM(GraphByMatrix* pg, ElemType v1, ElemType v2, int weight)

void printMatrix(GraphByMatrix* pg)

返回类型:GraphByMatrix*;

是否有参数:char* file,(文件名)

步骤:

  1. 根据固定的文件格式读取对应信息
  2. 第一个整数为顶点的个数,空格分割后是一个字符串,是顶点,然后一个回车,每行一条边,边的格式为起始顶点 + 空格 + 目的顶点 + 空格 + 权重
  3. 定义getIndexM函数,获取顶点在邻接矩阵中的下标
  4. 定义createGByM函数通过文件构造不包含权重的邻接矩阵初始化版本
  5. 定义addEdgeM函数给邻接矩阵增加边,循环读取文件直到结束
  6. 读取文件结束后,返回邻接矩阵
  7. 通过printMatrix函数打印邻接矩阵

算法时间复杂度:

  • 时间复杂度为O(N);
  • 空间复杂度为O(N2);

2.2.2 读文件建立邻接链表并输出

Node* newNode(int src, int dest, int weight);

GraphByList* createGByL(ElemType* arrayV, int n, int flag);

GraphByList* createLByFile(char* file);

int getIndexL(GraphByList* pg, ElemType v);

void addEdgeL(GraphByList* pg, ElemType src, ElemType dest, int weight);

void printList(GraphByList* pg);

返回类型:GraphByList*;

是否有参数:char* file,(文件名)

步骤:

  1. 根据固定的文件格式读取对应信息
  2. 第一个整数为顶点的个数,空格分割后是一个字符串,是顶点,然后一个回车,每行一条边,边的格式为起始顶点 + 空格 + 目的顶点 + 空格 + 权重
  3. 定义getIndexL函数,获取顶点在邻接表中的下标
  4. 定义createGByL函数通过文件构造不包含权重的邻接表初始化版本,即数组的值都为null,暂无链表的情况
  5. 定义addEdgeL函数,为邻接表增加边,在顶点对应下标的数组的链表上头插一个节点,这个节点由newNode函数构造
  6. 文件读完后返回邻接表
  7. 通过printList函数打印邻接表

算法时间复杂度:

  • 时间复杂度为O(N);
  • 空间复杂度为O(N);

2.2.3. 按深度优先遍历(DFS)算法输出图 G 中顶点的遍历序列

void dfs(GraphByMatrix* pg, ElemType v);

void dfsOrder(GraphByMatrix* pg, int src, int* isVisited);

返回类型:无返回值;

是否有参数:有,GraphByMatrix*邻接矩阵,char 起始顶点

步骤:

  1. 定义一个数组标记顶点是否被打印过,调用getIndexM函数获取顶点对应的下标
  2. 调用递归函数dfsOrder,传入邻接矩阵,下标,标记数组
  3. 打印过的则不能再打印,由于是连通图,所以任何一个顶点都能够打印所有顶点
  4. 一直递归至结束,回到dfs函数,释放临时的空间

算法时间复杂度:

  • 时间复杂度为O(N);
  • 空间复杂度为O(N);

2.2.4. 按广度优先遍历(BFS)算法输出图 G 中顶点的遍历序列

导入一个队列结构体Queue与其基本方法 ;(见附件)

void bfs(GraphByMatrix* pg, ElemType v);

返回类型:无返回值;

是否有参数:有,GraphByMatrix*邻接矩阵,char 起始顶点

步骤:

  1. 定义一个队列对象
  2. 定义一个数组标记顶点是否被打印过,调用getIndexM函数获取顶点对应的下标
  3. 让下标入队列
  4. 进入循环,出队列一个元素并打印,标记数组标记其打印过,循环此顶点所连顶点入队列,并标记数组为打印过(两层保证,防止打印多次)
  5. 直到队列为空,循环结束,释放临时的空间

算法时间复杂度:

  • 时间复杂度为O(N);
  • 空间复杂度为O(N);

2.2.5. 使用 Prime 算法输出图 G 的 最小生成树

导入一个优先级队列结构体PriorityQueue与其基本方法 ;(见附件)

定义一个Edge类,代表边

typedef struct Edge {
	int src;
	int dest;
	int weight;
}Edge;
Edge newEdge(int src, int dest, int weight) {
	Edge e = { src, dest, weight };
	return e;
}

void prime(GraphByMatrix* pg, ElemType v);

无返回值,有参数,邻接矩阵和起始顶点

步骤:

  1. 获取顶点下标
  2. 申请两部分空间,一部分代表起始顶点集合,一部分代表目的顶点集合,并进行初始化
  3. 定义一个优先级队列,并将v对应的所有边都入堆
  4. 进入循环,取出堆顶元素,如果这条边的起始顶点不存在于起始顶点集合,则可以打印这条边,打印后将此起始顶点加入起始顶点集合,目的顶点集合删除这个顶点,将此目的顶点的所有边都入堆
  5. 直到堆为空或者打印的边数为n-1,退出循环,释放临时的资源

复杂度分析:

时间复杂度:O(N2)

空间复杂度:O(N)

2.2.6. SPFA最短路径

导入队列结构体Queue及其基本方法;(见附件)

void SPFA(GraphByMatrix* pg, ElemType src, int* dist, int* path)

void printPath(GraphByMatrix* pg, int* dist, int* path, int srcIndex)

void printMinPath(GraphByMatrix* pg, ElemType v);

无返回值,传入邻接矩阵,起始顶点,dist最短路径长数组,path路径数组

步骤:

  1. 在printMinPath中,定义dist和path数组
  2. 调用SPFA函数,传入dist和path
  3. 在SPFA函数中,对dist和path进行初始化
  4. 定义一个队列
  5. 将起始顶点入队列
  6. 进入循环,每次取出队头元素,进行松弛操作,松弛成功,将其目的顶点入队列
  7. 直到队列为空
  8. 调用printPath打印最短路径

复杂度分析:

时间复杂度:O(NM)

空间复杂度:O(N)

2.2.7. 输出各顶点的入度和出度

void printDevOfV(GraphByMatrix* pg, ElemType v)

void printDevs(GraphByMatrix* pg, ElemType v)

无返回值,传入邻接矩阵,无意义参数v(故意构造这个参数列表,后面可以用函数指针数组节省代码长度)

步骤:

  1. 循环n次,调用printDevOfV函数,传入邻接矩阵和顶点
  2. 获取顶点下标,计算其入度边数和出度边数,在这里要区分无向图和有向图~

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(1)

2.2.8. Floyd 算法求每对顶点之间的最 短路径;

void floydWarShall(GraphByMatrix* pg, int** dist, int** path);

void printMinPaths(GraphByMatrix* pg, ElemType v);

无返回值,传入邻接矩阵,无意义参数v(故意构造这个参数列表,后面可以用函数指针数组节省代码长度)

步骤:

  1. 定义二维数组dist和二维数组path
  2. 掉头floydWarShall函数,传入这些参数,在这个函数中,首先要对二维数组初始化
  3. 在经历三层循环(最外层循环是中间节点),进行n3次松弛
  4. 循环结束返回到printMinPaths函数,循环n次调用printPath函数打印每一个顶点的最短路径
  5. 最终释放临时资源

复杂度分析:

时间复杂度:O(N3)

空间复杂度:O(N2)

2.2.9. 主函数菜单和测试设计

#include "basis.h"


void menu1() {
	printf("***************************\n");
	printf("0. 退出\n");
	printf("1. 有向图示例\n");
	printf("2. 无向图示例\n");
	printf("***************************\n");
}

void menu2() {
	printf("***************************\n");
	printf("0. 退出\n");
	printf("1. 输出邻接矩阵\n");
	printf("2. 输出邻接表(出度表)\n");
	printf("3. DFS\n");
	printf("4. BFS\n");
	printf("5. 获取最小生成树\n");
	printf("6. 获取最短路径\n");
	printf("7. 获取顶点的入度和出度\n");
	printf("8. 获取多元最短路径\n");
	printf("***************************\n");

}
void test(char* file) {
	GraphByMatrix* pg1 = createMByFile(file);
	GraphByList* pg2 = createLByFile(file);
	void(*p[])(GraphByMatrix*, ElemType) = {
		NULL, NULL, NULL, dfs, bfs, prime,
		printMinPath, printDevs, printMinPaths
	};
	int input = 0;
	do {
		ElemType V = 'A';
		menu2();
		scanf("%d", &input);
		if (input < 0 || input > 8) {
			printf("请重新输入");
			continue;
		}
		switch (input) {
			case 0 :
				printf("退出成功\n");
				break;
			case 1:
				printMatrix(pg1);
				break;
			case 2:
				printList(pg2);
				break;
			default:
				printf("输入起始点:");
				do {
					scanf("%c", &V);
				} while (V == '\n' || V == ' ');
				
			case 7:
			case 8:
				p[input](pg1, V);	
				printf("\n");
				break;
		}
	} while (input);
}

int main() {


	int input = 0;
	do {
		menu1();
		scanf("%d", &input);
		switch (input) {
			case 0:
				printf("退出成功\n");//由于此次后程序就结束了,所以就不free了
				break;
			case 1:
				test("有向图.txt");
				break;
			case 2:
				test("无向图.txt");
				break;
			default:
				printf("请重新输入\n");
				break;

		}

	} while (input);

	return 0;
}

返回int是与否,传入二叉树根节点

步骤:

  • 选择有向图或者无向图实例,然后进行测试

2.2.10. 层序遍历序列构造二叉树(用2.2.9的类似操作)

Node* createTreeByLevelOrder(char string[]);

返回二叉树根节点,传入字符数组

步骤:

  1. 只要数组不为空,就先入队数组首元素,并用这个值创建二叉树的root。
  2. 然后进入循环,循环条件为队列不为空,取出队头元素,队头出队。
  3. 只要数组还有元素,就先给刚刚拿出的对头元素创建左孩子,然后左孩子入队。
  4. 同上,再创建右孩子,右孩子入队。
  5. 结束一次循环。回到2

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(log2N)

2.3. 程序运行过程及结果

在这里插入图片描述
在这里插入图片描述

5. 总结

  • 在这个过程中遇到很多问题,例如空指针异常,结果与预计结果不符
  • 但是只要好好调试,总是能解决问题

6. 附录:源代码

代码仓库地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/581585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

威胁情报如何改进 DDoS 保护

分布式拒绝服务 (DDoS) 攻击已成为各种企业的主要威胁&#xff0c;从最小的跨国公司到最大的跨国公司。 根据 2022年全球威胁分析报告&#xff0c;恶意DDoS攻击较2021年增长了150%。此外&#xff0c;DDoS攻击的频率也出现显着上升&#xff0c;令人担忧。 在全球范围内&#x…

港大、南大、清华抢先开源「复刻」版DragGAN,开箱即用!

来源 |新智元 还记得前几天发布的DragGAN吗&#xff1f; 没错&#xff0c;就是那个「轻轻点两下」1秒修图的工具。 ▲拍的照片表情不好&#xff1f;修&#xff01;脸型不够瘦&#xff1f;修&#xff01;脸冲镜头的角度不对&#xff1f;修&#xff01; ▲搞不好&#xff0c;「让…

https 建立连接过程分析

从真实的抓包开始 根据抓包结果可以看到 从客户端发起请求开始&#xff0c;主要经过以下几个过程&#xff1a; 1、TCP 三次握手 2、浏览器发送 Client Hello 到服务器 3、服务器发送Server Hello 、证书、证书状态、服务端密钥交换&#xff0c;到浏览器 4、浏览器发送 客户端密…

【Linux】搭建SFTP文件服务器

一、协议介绍1.1 FTP 协议1.11 特点1.12 基本工作原理 1.2 SFTP协议1.21 特点1.22 基本工作原理 1.3 ssh协议1.31 特点1.32 基本工作原理 1.4 其他常见文件传输协议 二、搭建Linux的SFTP文件服务器三、连接测试3.1 电脑连接3.2 手机连接 一、协议介绍 1.1 FTP 协议 1.11 特点…

chatgpt赋能python:Python如何实现不换行

Python如何实现不换行 Python是一种高级编程语言&#xff0c;它的应用领域非常广泛&#xff0c;尤其是在数据分析、人工智能、网络爬虫等领域中拥有广泛的应用。而在Python中&#xff0c;有时候需要控制输出内容的样式&#xff0c;比如在输出时避免出现换行&#xff0c;这个需…

Bootstrap中的js插件使用

1. 标签页 1.1 init <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevic…

sqli_labs21-23

less21 这题是cookie注入&#xff0c;如果不登录进去是看不到cookie信息的&#xff0c;所以我们要先登录进去 用户admin&#xff0c;密码admin 抓包后发现cookie字段有base64编码信息&#xff0c;选中后burp自动检测翻译 既然是cookie注入并且字段是被加密了的&#xff0c;说明…

chatgpt赋能python:Python中5/2问题引发的思考

Python中5/2问题引发的思考 在Python中&#xff0c;我们常常遇到数字计算的问题&#xff0c;比如5/2。当我们在Python中执行如下代码时&#xff1a; print(5/2)输出结果为2.5。 但是在其他编程语言中&#xff0c;比如C和Java&#xff0c;同样的计算结果是2&#xff0c;而不是…

chatgpt赋能python:Pythonnumpy库下载教程:学习数据分析必备工具

Python numpy库下载教程&#xff1a;学习数据分析必备工具 介绍 Python是一种优秀的脚本语言&#xff0c;常用于数据分析、机器学习等领域&#xff0c;而Numpy是Python中最基础的科学计算库&#xff0c;提供了大量针对数组及矩阵操作的函数和方法。然而&#xff0c;对于初学者…

jQuery元素操作和尺寸位置

1. 遍历元素 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width,…

Kubernetes_核心组件_kubelet_kubelet服务全解析

文章目录 前言一、查看kubelet当前运行1.1 查看kubelet当前运行1.2 kubelet配置文件1.3 kubelet启动参数文件1.4 kubelet启动全过程 (自定义启动参数文件) 二、kubelet启动过程2.1 kubelet启动过程2.2 自定义kubelet所有文件并运行步骤1&#xff1a;新建静态token文件和user&am…

Fourier分析入门——第11章——Fourier变换

目录 第11章 Fourier变换(Transform) 11.1 引言 11.2 逆向正弦和余弦变换(The Inverse Cosine and Sine Transforms) 11.3 正向正弦和余弦变换(The Forward Cosine and Sine Transforms) 11.4 离散谱对比谱密度(Discret spectra vs. spectral density) 11.5 Fourier变换的…

chatgpt赋能python:Python中4.5/2等于多少?——解密Python的除法运算

Python中4.5/2等于多少&#xff1f;——解密Python的除法运算 Python作为一种常用的编程语言&#xff0c;在业界有着广泛的应用。而除法是Python中常用的运算之一。但是&#xff0c;当我们输入4.5/2时&#xff0c;会得到什么样的结果呢&#xff1f;这篇文章将解密Python的除法…

Spring Boot:从入门到实践的全面指南

文章目录 1. Spring Boot简介及特性1.1 简介&#xff1a;什么是Spring Boot1.2 特性&#xff1a;Spring Boot的优势与特点1.3 四大核心&#xff1a;Spring Boot的核心组成 2. Spring Boot入门案例2.1 Spring Boot项目开发步骤2.2 创建一个Spring MVC的Spring Boot Controller2.…

chatgpt赋能python:Python不能参加奥赛的原因

Python不能参加奥赛的原因 Python 是一种广泛使用的高级编程语言&#xff0c;以其简单易学、可读性高等特点受到了众多程序员的喜爱&#xff0c;但是它在国际奥林匹克竞赛中并不被允许参赛。本文将会介绍 Python 不能参加奥赛的原因&#xff0c;并且分析该限制是否合理。 原因…

使用 GitHub Actions 自动部署 Hexo 个人博客

文章目录 申请 GitHub Token源码仓库配置 Github Action重新设置远程仓库和分支查看部署 每次部署 Hexo 都需要运行 hexo cl & hexo g & hexo d 指令三件套完成推送到远程仓库&#xff0c;随着文章越来越多&#xff0c;编译的时间也会越来越长&#xff0c;通过 Github …

前缀树概念

前缀树&#xff08;prefix tree&#xff09; 准备一个Str[]&#xff0c;数组中元素有[“abc”,“bcd”,“abg”,“bcde”,“qwe”]&#xff0c;如何将数组中元素加到树中呢&#xff1f; 从最开始的字符串abc说&#xff0c;第一个字符是a&#xff0c;从一个空的头节点出发&#…

jQuery属性操作和内容文本值

1. 属性操作 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width,…

全局CSS样式1

1. 响应式基础 1.1 init <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compati…

chatgpt赋能python:Python两行三列列表:更高效的数据展示方式

Python 两行三列列表&#xff1a;更高效的数据展示方式 在Python编程中&#xff0c;经常会需要对数据进行展示。而最常见的方式则是使用列表。然而&#xff0c;当数据量较大时&#xff0c;传统的列表显示方式显得过于冗长。Python两行三列列表的使用&#xff0c;不仅可以增加代…