【数据结构】二叉树的构建与基本操作实现

news2024/11/24 3:30:38

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》

🌝每一个不曾起舞的日子,都是对生命的辜负


目录

前言

1.前序建立二叉树

2.销毁二叉树

3.统计

4.查找值为x的节点

5.前中后序遍历

6.层序遍历

7.判断二叉树是否为完全二叉树

总结


前言

本篇文章博主将会与大家一起学习二叉树的构建与一些基本操作实现,那么对于二叉树来说:递归是不可绕开的话题,在本篇文章中你会看到很多的递归,递归的核心思想就是分割子问题,他异于我们之前学习的遍历枚举等思想,所以希望你能有所收获🌍


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================

GITEE相关代码:🌟fanfei_c的仓库🌟

=========================================================================


1.前序建立二叉树

首先我们输入一段前序序列,用字符串存储,空节点的部分我们用字符'#' 替代,利用*pi作为下标,遍历该字符串。

前序:将字符赋给当前节点的数据域,*pi的值+1,给当前节点的左孩子执行相同操作,给当前节点的右孩子执行相同操作,采用递归方式完成。

那么你可以写出中序建立二叉树的算法么?

代码实现:

BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	root->_data = a[(*pi)++];

	root->_left = BinaryTreeCreate(a, pi);
	root->_right= BinaryTreeCreate(a, pi);
	return root;
}

为什么使用指针pi,而不用整型pi? 

  • 若传整型,在函数递归的过程中,由于函数栈帧的关系,整型pi的值不会保留,所以需要传递指针。

2.销毁二叉树

销毁二叉树比较简单。

注意:销毁二叉树采用的是后序遍历的方式,因为如果先销毁根节点,那么就找不到孩子节点了。 

代码实现: 

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root)
	{
		BinaryTreeDestory(&(*root)->_left);
		BinaryTreeDestory(&(*root)->_right);
		free(*root);
        *root = NULL;
	}
}

3.统计

我们主要看一下统计第k层节点的算法:

如何判断何时递归到第k层呢?

  • 想要到达第k层,我们可以利用递归每次让k-1,当k的值为1的时候,那么此时我们就来到了第k层
  • 可以来到第k层的,也就是k==1时我们返回1
  • 递归还是采用双路递归,即返回左右子树的和即可

代码实现: 

//计算节点数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

//计算叶子节点数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->_left == NULL && root->_right == NULL)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->_left) 
        + BinaryTreeLeafSize(root->_right);
}

// 二叉树第k层节点个数(假设从第1层开始)
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->_left, k-1) 
        + BinaryTreeLevelKSize(root->_right, k-1);
    //递归每次让k-1,当k的值为1的时候,那么此时我们就来到了第k层
}

//二叉树深度
int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return fmax(BinaryTreeHeight(root->_left)
        , BinaryTreeHeight(root->_right))+1;
}

4.查找值为x的节点

该递归过程可以理解为前序思想,即如果根节点的值为x,那么肯定就可以直接返回,如果不同,我们就可以向左子树和右子树方向考虑,具体的设计可见代码。

代码实现:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->_data == x)
	{
		return root;
	}
	BTNode* ret = NULL;
	ret = BinaryTreeFind(root->_left,x);
	if (ret)
	{
		return ret;
	}
	ret = BinaryTreeFind(root->_right, x);
	if (ret)
	{
		return ret;
	}
	return NULL;
}

5.前中后序遍历

代码实现:

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_left);
	BinaryTreeInOrder(root->_right);
}

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->_left);
	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_right);
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->_left);
	BinaryTreeInOrder(root->_right);
	printf("%c ", root->_data);
}

6.层序遍历

层序遍历的实现需要用到队列的逻辑结构。

首先将根节点入队,输出队首元素,并根据根节点找到左右孩子节点并入队,然后出队根节点,此时队首元素更新为根节点的左子树,再以此左子树为根循环这个过程,就能成功实现层序遍历。

代码实现:

void BinaryTreeLevelOrder(BTNode* root)
{
	Que q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->_data);
		if (front->_left)
		{
			QueuePush(&q, front->_left);
		}
		if (front->_right)
		{
			QueuePush(&q, front->_right);
		}
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return;
}

7.判断二叉树是否为完全二叉树

完全二叉树通俗的讲就是每个节点都是连续的,不存在某个节点之前存在空节点的情况,那么根据这个特性,我们同样可以利用层序遍历的思想,利用队列的逻辑结构来解决。

思路:与层序遍历不同的是,我们不管左右子树是否为空都入队,这样在循环结束时,队列中要么全为空,此时证明该二叉树是完全二叉树,如果队列中但凡存在一个不为空的情况,那就证明该二叉树不为完全二叉树。

代码实现:

bool BinaryTreeComplete(BTNode* root)
{
	Que q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
		QueuePop(&q);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

注意:循环条件中QueueEmpty判断的是队列是否为空,即队首指针指向是否为空,而第一次遍历队列判断的条件是队首元素的数据域是否为NULL,两者不一样。 


总结

递归是一种十分巧妙的方法,他的特点是可以拆分子问题,将大问题简化为可以解决小问题,但不知道你是否和我一样,思考问题好像已经习惯了遍历枚举求解的思想,那么下一篇文章我会为大家带来二叉树的OJ题系列,强化对于递归问题的理解,让我们一起努力吧🌝

=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

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

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

相关文章

uni-app 之 picker选择器

uni-app 之 picker选择器 同步滚动&#xff1a;开 uni-app 之 picker选择器 一、普通选择器 二、多列选择器 三、时间选择器 四、日期选择器 一、普通选择器 <template><view><picker change"bindPickerChange" :value"index" :range&q…

LabVIEW开发航天器模拟器的姿态控制和反作用轮动量管理

LabVIEW开发航天器模拟器的姿态控制和反作用轮动量管理 在过去十年中&#xff0c;航天器一直是现代技术进步的先决条件。迄今为止&#xff0c;为了更好地完成各种实际任务&#xff0c;已经在航天器姿态控制领域进行了大量研究。航天器一旦进入太空&#xff0c;就容易出现不确定…

JavaWeb开发-05-SpringBootWeb请求响应

一.请求 1.Postman 2.简单参数 ​ package com.wjh.controller;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;/** 测试请求参数接受*/ R…

【IntelliJ IDEA】cmd和idea Terminal查看java版本不一致

问题描述 原来win10电脑上安装的是jdk8的版本&#xff0c;因某些原因&#xff0c;现在想换成jdk7的版本&#xff0c;修改环境变量后&#xff0c;在cmd中执行 [java -version]命令&#xff0c;显示的是7的版本。 但在idea的Terminal中执行&#xff0c;确实显示8的版本。 原因分…

vue设置路由模式为history,打包部署,并解决404问题

现在Router配置里面加上 base 和 mode 属性&#xff1a; export default new Router({base: /your_project_name/,mode: history,routes: [......] })这样就能支持 history 模式了&#xff0c;但是现在静态资源获取还有问题。 解决静态资源获取问题 在 config/index.js 文件…

TS中的数据类型

一、number类型 let c: number; c 10; c "hello"; // 不能复制string类型 二、string类型 let d: string; d "hello"; d 10; // 不能复制number类型 三、boolean类型 let e: boolean true; e false; e 10; // 不能赋值true和false以外的值 四…

Cypress安装与使用教程(1)—— 软测大玩家

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

半导体划片机工艺应用

半导体划片工艺是半导体制造过程中的重要步骤之一&#xff0c;主要用于将大尺寸的晶圆切割成小片&#xff0c;以便进行后续的制造和封装过程。以下是一些半导体划片工艺的应用&#xff1a; 晶圆划片&#xff1a;在半导体制造过程中&#xff0c;需要将大尺寸的晶圆切割成小片&am…

【数据库系统概论】数据库系统外部的体系结构

单用户结构主从式结构分布式结构客户机&#xff0f;服务器结构&#xff08;C/S结构&#xff09;浏览器 / 服务器结构&#xff08;B/S结构&#xff09;感谢 &#x1f496; 上一篇文章 数据库系统的三级模式和二级映射介绍的是数据库系统内部的体系结构&#xff0c;是从应用开发…

计算机网络知识补充(1)

计算机网络:是一个将分散的&#xff0c;具有独立功能的计算机系统&#xff0c;通过通信设备和线路进行连接起来&#xff0c;由功能完善的软件实现资源共享和信息共享的系统&#xff0c;计算机网络是互连的&#xff0c;自治的计算机集合 互连:通过通信链路来进行互联互通 自治:没…

云安全威胁和责任

云计算的共享特性和按需定制本质除了给企业带来效率上提升&#xff0c;也引入了新的安全威胁&#xff0c;有可能使企业得不偿失。 之前云安全联盟(CSA)的报告便指出&#xff0c;云服务天生就能使用户绕过公司范围内的安全策略&#xff0c;建立起自己的影子IT项目服务账户。 新的…

全面感知,智能预警!燃气感知云,守护城市“烟火气”

燃气安全如何保障&#xff1f;燃气企业如何精准运营&#xff1f;天翼物联基于感知云平台创新能力&#xff0c;提供燃气感知云服务&#xff0c;包括泛协议接入、感知云燃气平台、燃气感知数据治理、决策处置大屏四大服务&#xff0c;构建燃气行业感知神经系统新型数字化底座&…

Interceptor的使用场景:拦截请求中的租户信息,注入到租户上下文中

业务场景 在SaaS环境中&#xff0c;租户是最重要的隔离业务数据的属性了&#xff0c;在自己的项目体系环境中&#xff0c;租户id能保证有值。但有个特殊场景&#xff0c;某些特殊权限的账号需要修改指定租户的内容&#xff0c;也即前端会携带租户信息过来&#xff0c;并且内部涉…

【力扣-每日一题】2560. 打家劫舍 IV

class Solution { public:bool check(vector<int> &nums,int max_num,int k){//只需要计算可以偷的房间。在满足最大值为max_num下时&#xff0c;能偷的最多的房间&#xff0c;与k值比较//如果大于K&#xff0c;说明max_num还可以缩小//如果小于看&#xff0c;说明ma…

深入解析容器与虚拟化:技术、对比与生态

深入解析容器与虚拟化&#xff1a;技术、对比与生态 文章目录 深入解析容器与虚拟化&#xff1a;技术、对比与生态容器和虚拟化的基本概念和原理容器的定义和特点虚拟化的定义和特点 容器使用场景容器和虚拟机的对比虚拟化技术的四个特点容器实现虚拟化的原理常见容器引擎和容器…

【Ubuntu配置ssh和sftp与windows的xshell连接】

【Ubuntu配置ssh和sftp与windows的xshell连接】 一、Ubuntu配置ssh1、查看是否已经安装2、安装openssh server3、修改端口 修改Port后的参数4、重启ssh5、查看状态6、查看端口 二、windows连接 一、Ubuntu配置ssh 1、查看是否已经安装 dpkg -l | grep ssh2、安装openssh serv…

IntelliJ IDEA学习总结(3)—— IntelliJ IDEA 常用快捷键(带动图演示)

一、构建/编译 Ctrl + F9:构建项目 该快捷键,等同于菜单【Build】—>【Build Project】 执行该命令后,IntelliJ IDEA 会编译项目中所有类,并将编译结果输出到out目录中。IntelliJ IDEA 支持增量构建,会在上次构建的基础上,仅编译修改的类。 Ctrl + Shift + F9:重新编…

程序员戴什么样的眼睛比较好

眼镜https://baijiahao.baidu.com/s?id1770288495355869186&wfrspider&forpc

【JDK 8-集合框架】5.3 limit 和 sorted 函数

一、sorted 函数 二、limit 函数 三、实战 执行结果&#xff1a; 一、sorted 函数 对流进行自然排序&#xff0c;其中的元素必须实现Comparable 接口 sorted(Comparator<? super T>comparator) 用来自定义升降序 二、limit 函数 获取指定数量的元素 (limit(long …

《机器学习基石前四章复习》

【引言】 训练样本D和最终测试h的样本都是来自同一个数据分布&#xff0c;这是机器能够 学习的前提。另外&#xff0c;训练样本D应该足够大&#xff0c;且hypothesis set的个数是有限的&#xff0c;这样 根据霍夫丁不等式&#xff0c;才不会出现Bad Data&#xff0c;保证Ein≈…