实验 2:树形数据结构的实现与应用

news2024/12/23 13:41:52
  • 东莞理工学院的同学可以借鉴,请勿抄袭

1.实验目的

通过实验达到:

  1. 理解和掌握树及二叉树的基本概念;

  2. 理解和掌握二叉树的顺序存储结构、链式存储结构;

  3. 理解和掌握采用二叉链式存储结构下二叉树的各种遍历操作的思想及 其应用;

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

  5. 掌握典型二叉树操作算法的算法分析。

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

设树结点的元素类型为 ElemType(可以为 char 或 int),采用二叉链(或三叉 链,即双亲孩子)存储,实现以下二叉树的各种基本操作的程序:

① 编写一个创建二叉树的函数,通过文件读取方式,建立不少于 10 个结点 的二叉树 T(建议用递归方式创建);

② 给定元素 x,在二叉树中查找该元素 x,若找到则返回该结点的指针;

③ 用凹入表示法打印该二叉树(可以是图 8-2 的形式或者 8.4.3 中的逆时针 旋转 90°,一个先序,一个中序 RDL);

④ 用非递归方式先序遍历方式输出树 T 的结点;(用到栈)

⑤ 用中序或后序遍历方式输出树 T 的结点;

⑥ 用层次遍历方式输出树 T 的结点;(用到队列)

⑦ 输出树 T 的深度;

⑧ 输出树 T 的叶子结点或非叶子结点;

⑨ 主函数设计菜单,通过菜单选择相应的函数调用实现以上各项操作。(在 实验报告中请画出测试的二叉树。) 附加题:(每完成一个额外附加 5 分,上限 10 分)

① 8-32 判断该二叉树是否为完全二叉树(层次遍历);

② 8-34 根据顺序存储建立二叉链存储的二叉树(与第 1 个操作类似);

③ 哈夫曼树编码问题的设计和实现(双亲孩子表示法+flag)。

一个表达式二叉树的例子:在这里插入图片描述

2.1. 数据结构设计

typedef char ElemType;

typedef struct Node {
	ElemType value;
	struct Node* left;
	struct Node* right;
}Node;

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

2.2.1. 读文件建立树函数

Node* createTreeByFile();

Node* createTreeByOrder(char string[]);

Node* createNode();

返回类型:Node*;

是否有参数:无

步骤:

  1. fopen打开文件读取字符串,此字符串是这种格式:“-+a##*b##-c##d##/e##f##”
    • (麻烦老师说清楚点,我都不知道你是要我以层序遍历序列构造,还是以前中序或者后中序序列构造,还是以这种来构造…)
  2. 定义一个全局变量j记录字符串被读取到哪个下标
  3. 进入函数后,判断j下标是否为“#”,是则返回NULL,如果不是,调用createNode构造一个节点root
  4. 递归调用一次,将其赋值给root左孩子
  5. 再递归调用一次,将其赋值给root右孩子
  6. 返回root

算法时间复杂度:

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

2.2.2 返回x值对应节点的指针函数

Node* findNode(Node* root, ElemType value);

返回类型:Node*;

是否有参数:二叉树的根节点,键值x

步骤:

  • root为NULL直接返回
  • root对应值为x,返回root
  • 递归调用左孩子,返回的值不为空则返回左孩子指针
  • 递归调用右孩子,返回的值不为空则返回右孩子指针
  • 否则返回空,没有找到

算法时间复杂度:

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

2.2.3. 凹入法打印二叉树函数

void incurvatePrint(char preorder[], char DRLorder[]);

buildTree(preorder, DRLorder);

Node* buildByIndex(char preorder[], char inorder[], int start, int end);

void print(Node* root, int n);

int search(char inorder[], int start, int end, char key);

返回类型:无返回值;

是否有参数:有,传入前序序列与DRL序列

步骤:

  1. incurvatePrint函数内调用buildTree函数获取二叉树,传入两个序列
  2. 调用buildByIndex函数获取二叉树,传入两个序列,以及0和strlen(inorder) - 1(from,to)
  3. 前序遍历序列的第一个值,就是根节点,在DRL中找到这个根节点下标,左边为右子树,右边为左子树,分别递归调用buildByIndex函数
  4. from > end 返回NULL
  5. 最终incurvatePrint调用print函数,传入root和0(n)用凹入法打印二叉树
  6. 每进入一层递归,传入的第二个参数都会加1
  7. 在打印root对应值之前,要递归函数,传入右孩子和n+1,之后要打印n个缩进,打印root值后打印回车符,调用递归函数,传入左孩子和n+1

算法时间复杂度:

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

2.2.4. 非递归先序打印树函数

栈基本函数:

typedef struct Stack {
	int size;
	int capacity;
	Node** arr;
}Stack;
Stack newStack() {
	Node** arr = calloc(N, sizeof(Node*));
	Stack stack = { 0, N, arr };
	return stack;
}
void push(Stack* ps, Node* value) {
	if (ps->size == ps->capacity) {
		ps->arr = (Node**)realloc(ps->arr, 2 * ps->size * sizeof(Node*));
		ps->capacity *= 2;
	}
	ps->arr[ps->size] = value;
	(ps->size)++;
}
Node* pop(Stack* ps) {
	if (ps->size == 0) {
		printf("666,空了\n");
		return NULL;
	}
	return ps->arr[--(ps->size)];
}
int isEmptyStack(Stack* ps) {
	return ps->size == 0;
}

void preorderNormal(Node* root);

返回类型:无返回值;

是否有参数:有,传入二叉树根节点

步骤:

  1. 建立栈,打印根节点root,(为NULL则return)
  2. 定义cur为root的左孩子
  3. 进入循环,条件:栈不为空或者cur不为空
  4. 进入循环:如果左孩子不为空,压栈并打印节点对应值,cur=cur->left
  5. 循环停止cur赋值为栈弹出来的节点的右孩子
  6. 进入循环判断
  7. 结束循环,则打印结束

算法时间复杂度:

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

2.2.5. 中序遍历和后序遍历函数

void inorder(Node* root);

void postorder(Node* root);

无返回值,有参数,二叉树根节点

步骤:

  1. 根节点为NULL不打印
  2. 依次调用递归函数,传入左孩子和右孩子
  3. 中序遍历则是在两次调用之间,后序遍历则是两次调用之后

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(logN)

2.2.6. 层序遍历函数

常用队列函数:

typedef struct Queue {
	DataType* arr;
	int size;
	int capacity;
}Queue;
Queue createQueue() {
	DataType* arr = (DataType*)calloc(N, sizeof(DataType));
	Queue queue = { arr, 0, N };
	return queue;
}

void offer(Queue* pq, DataType value) {
	if (pq->size == pq->capacity) {
		pq->arr = (DataType*)realloc(pq->arr, 2 * pq->size * sizeof(DataType));
		pq->capacity *= 2;
	}
	pq->arr[pq->size] = value;
	(pq->size)++;
}

DataType poll(Queue* pq) {
	if (isEmptyQueue(pq)) {
		printf("队列空\n");
		exit(0);
	}
	DataType ret = pq->arr[0];
	(pq->size)--;
	memmove(pq->arr, pq->arr + 1, pq->size * sizeof(DataType));
	return ret;
}

DataType peek(Queue* pq) {
	if (isEmptyQueue(pq)) {
		printf("队列空\n");
		exit(0);
	}
	return pq->arr[0];
}

int isEmptyQueue(Queue* pq) {
	return pq->size == 0;
}

void levelOrder(Node* root);

无返回值,传入参数为二叉树根节点

步骤:

  1. 根节点为NULL,则return
  2. 建立队列queue,根节点入队列
  3. 进入循环,条件:队列不为空
  4. Node* tmp接受出队列元素
  5. 打印tmp的值,将tmp的左孩子如队列,右孩子入队列(如果为NULL则不用)
  6. 直到出循环

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(log2N)

2.2.7. 输出树的深度

int getHeight(Node* root);

返回int深度,传入二叉树根节点

步骤:

  1. root为NULL返回
  2. 调用两次递归函数,依次传入左孩子和右孩子
  3. 返回两个递归函数的返回值中的最大值加1

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(log2N)

2.2.8. 输出树 T 的叶子结点或非叶子结点

void printLeafNode(Node* root);

void printNotLeaf(Node* root);

无返回值,传入二叉树根节点

步骤:

  1. root为NULL,return
  2. root不为NULL,
    1. printLeafNode函数如果root左右孩子都为NULL,则打印
    2. printNotLeaf函数中如果root左右孩子不全为NULL,则打印
  3. 调用两次递归函数,依次传入左孩子和右孩子

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(log2N)

2.2.9. 判断该二叉树是否为完全二叉树

int isCompleteTree(Node* root)

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

步骤:

  1. 如果root为NULL,返回1
  2. 定义队列queue,根节点入队列
  3. 进入循环,循环条件是queue不为空队列
  4. Node* cur接受出队列元素
  5. 如果cur不为NULL,左右孩子入队列
  6. 否则,一直出队列,如果队列中出现非NULL值,则返回0,否则返回1
  7. 若循环结束,返回1

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(log2N)

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

Node* createTreeByLevelOrder(char string[]);

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

步骤:

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

复杂度分析:

时间复杂度:O(N)

空间复杂度:O(log2N)

2.2.11. main函数


int main() {
	printf("===================================\n");
	Node* root = createTreeByFile();

	printf("===================================\n");
	Node* XTree = findNode(root, '*');

	printf("===================================\n");
	printf("凹入法打印:\n");
	incurvatePrint("-+a*b-cd/ef", "f/e-d-c*b+a");

	printf("===================================");
	printf("\n非递归前序:");
	preorderNormal(root);
	printf("\n递归前序:");
	preorder(root);
	printf("\nXTree前序:");
	preorder(XTree);

	printf("\n===================================");
	printf("\n递归中序:");
	inorder(root);
	printf("\n递归后序:");
	postorder(root);

	printf("\n===================================");
	printf("\n层序:");
	levelOrder(root);

	printf("\n===================================");
	printf("\n深度:%d", getHeight(root));

	printf("\n===================================");
	printf("\n叶子节点:");
	printLeafNode(root);
	printf("\n非叶子节点:");
	printNotLeaf(root);

	printf("\n===================================\n");
	printf("root%s完全二叉树", isCompleteTree(root) ? "是" : "不是");

	printf("\n===================================\n");
	levelOrder(createTreeByLevelOrder("123456789"));
}

2.3. 程序运行过程及结果

在这里插入图片描述

5. 总结

  • 在这个过程中遇到很多问题,例如空指针异常,结果与预计结果不符
  • 但是只要好好调试,总是能解决问题
  • 对于递归的重点就是转化为子问题,整体性的思想
  • 非递归实现则需要结合栈或者队列!

6. 附录:源代码

代码地址:码云连接

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

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

相关文章

详解Jetpack Compose的标准布局

前言 Compose是一个声明式UI系统,其中,我们用一组函数来声明UI,并且一个Compose函数可以嵌套另一个Compose函数,并以树的结构来构造所需要的UI。 在Compose中,我们称该树为UI 图,当UI需要改变的时候会刷新…

UML类图与设计模式/原则

目录 类之间的关系依赖泛化(继承)实现关联聚合组合 设计模式的七大原则设计模式单例工厂模式原型模式(深/浅拷贝)建造者模式适配器模式桥接模式装饰者模式组合模式外观模式享元模式代理模式模板方法模式命令模式访问者模式迭代器模式观察者模式中介者模式…

chatgpt赋能Python-python3_8_5安装后怎么打开

Python3.8.5安装后怎么打开:简易步骤指南 Python是一种高级编程语言,被广泛用于数据分析、机器学习、自动化、网络编程等多个方面。Python语言优雅简洁,易于上手,是编程初学者的最佳入门语言之一。本文将介绍Python3.8.5版本的安…

C++ 红黑树(更新中)

前言 上篇博客学习了平衡二叉搜索树(AVLTree),了解到AVL树的性质,二叉搜索树因为其独特的结构,查找、插入和删除在平均和最坏情况下都是O(logn)。AVL树的效率就是高在这个地方。 但是在AVL树中插入或者删除结点,使得高度差的绝对…

学系统集成项目管理工程师(中项)系列22c_信息化知识(下)

1. 新一代信息技术对产业的推动 1.1. 加快建设宽带、泛在、融合、安全的信息网络基础设施,推动新一代移动通信、下一代互联网核心设备和智能终端的研发及产业化,加快推进三网融合,促进物联网、云计算的研发和示范应用 1.2. 大数据 1.2.1. …

申请GPT-4插件,等待GPT-4插件候补全过程

前言 GPT4相信大家都知道它的升级是带来更多惊喜的,目前GPT4已经推出了网页和插件功能,这些插件是专门为语言模型设计的工具。插件可以帮助 ChatGPT 访问最新信息、运行计算或使用第三方服务。写文记录一下,如果你现正好有需要GPT-4 插件的需…

基于springboot就业信息管理系统

开发技术与环境配置 以Java语言为开发工具,利用了当前先进的springboot框架,以MyEclipse10为系统开发工具,MySQL为后台数据库,开发的一个就业信息管理系统。 SpringBoot框架 SpringBoot是一个全新开源的轻量级框架。基于Spring…

chatgpt赋能Python-python3_8怎么打开

Python 3.8:如何下载和打开 Python 3.8是一种高级编程语言,被广泛应用于人工智能、数据分析和网络编程等领域。本文将介绍如何下载和打开Python 3.8以及其优点和用途。 下载Python 3.8 Python 3.8的下载地址为官方网站。根据不同操作系统的需求&#…

系统清理优化工具:CCleaner

哈喽,大家好。今天带各位小伙伴们学习一款系统清理优化工具——CCleaner。 CCleaner是一款系统优化和隐私保护工具。它可以用来清除Windows系统不再使用的垃圾文件,以腾出更多硬盘空间。也可以清除使用者的上网记录。它的体积小,运行速度快&…

java+springboot高校校友校园信息管理系统

本高校校友信息管理系统使用Web开发,运行在Internet环境之上,系统的后台编程语言使用JAVA,数据库使用MySQL。完成了两个用户角色的功能,管理员管理所有信息,前台学生用户登录后查看公告,在线捐赠申请&#…

docker是怎么决定容器内容存储到哪个目录的?(存储驱动决定的)(乱七八糟的)

文章目录 docker是怎么决定容器内容存储到哪个目录的?docker对我/var这个目录有没有什么要求,比如要求它的文件系统是指定的类型如果我Docker的默认存储驱动是overlay2,但是我/var目录的文件系统不是overlay2,这没影响吗&#xff…

chatgpt赋能Python-python3_7_1如何使用

Python 3.7.1使用指南 Python自从出现以来,已经成为了一个非常流行的编程语言,每年都会更新版本以满足不断变化的市场需求。其中最新版本是Python 3.7.1,它与其前身相比提供了许多改进和新功能,同时也解决了一些已知的问题。本文…

MobileNetV3详解及在pytorch下基于CIFAR10数据集的实现

1 MobileNetV3介绍 MobileNetV3 是由 google 团队在 2019 年提出的轻量化网络模型,传统的卷积神经网络,内容需求大,运算量大,无法再移动设备以及嵌入式设备上运行,为了解决这一问题,MobileNet网络应运而生。…

chatgpt赋能Python-python3_8下载numpy

Python3.8下载numpy:安装步骤与常见问题解决方案 Python3.8是最新版的Python编程语言,它提供了丰富的库和框架支持,包括科学计算库numpy。然而,有些用户可能会在安装numpy时遇到一些麻烦,本文将教你如何下载numpy&…

海康机器视觉工业相机客户端MVS-常用功能CCM

什么是CCM? CCM是一种功能。 CCM矩阵是通过对每一个RGB分量乘以一个校正矩阵来实现色彩校正。当图像经过白平衡处理后,图像整 体会显得比较黯淡,同时多种颜色可能存在不同程度地偏离其标准值。此时需要对图像的色彩乘以校正 矩阵来修正各颜色至其标准值,使图像的整体色彩更…

【智能算法1】模拟退火算法_Python实现

一、模拟退火算法(SA) 1.1 固体退火的原理 加热使得固体融化,然后缓慢地降低温度,以此来让固体内部的粒子排布更加均匀。 分为四个阶段: 升温阶段、降温阶段、等温阶段、达到目标温度退火完成 等温阶段就是在塑造…

chatgpt赋能Python-python3_8安装scrapy

Python3.8 安装 Scrapy 如果你是 Python 开发者,你可能已经听说过 Scrapy:一个开源框架,用于快速高效地抓取和提取网页数据。在本篇文章中,我们将介绍如何在 Python3.8 环境下安装 Scrapy,并解释该过程的每一个步骤。…

chatgpt赋能Python-python3_6怎么算

Python 3&6怎么算?—— Python版本的比较 Python是一款广泛使用的高级编程语言,已经有好几个版本了,其中比较常用的是Python 3和Python 2.7。近年来,Python 3越来越受欢迎,那么Python 3和6怎么算呢?本…

Doxygen源码分析: 根目录文件简要介绍

2023-05-18 22:54:02 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz 文章目录 1. doxygen 版本2. 文件介绍DockerfileLICENSE.dockerignore.codedocsVERSION.editorconfigLANGUAGE.HOWTOBUILD.txtINSTALL.gitignoreREADME.mdCMakeLists.txt 1. doxygen 版…

一图看懂 chardet 模块:字符编码检测器,兼容 Python2 和 Python3,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创,转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 chardet 模块:字符编码检测器,兼容 Python2 和 Python3,资料整理笔记(大全) 🧊摘要🧊模块…