【初阶数据结构】详解二叉树 - 树和二叉树(三)(递归的魅力时刻)

news2024/12/23 6:30:51

文章目录

  • 前言
  • 1. 二叉树链式结构的意义
  • 2. 手搓一棵二叉树
  • 3. 二叉树的遍历(重要)
    • 3.1 遍历的规则
    • 3.2 先序遍历
    • 3.3 中序遍历
    • 3.4 后序遍历
    • 3.5 遍历的代码实现
      • 3.5.1 先序遍历代码实现
      • 3.5.2 中序遍历代码实现
      • 3.5.3 后序遍历代码实现
  • 4. 统计二叉树结点的个数
  • 5. 统计二叉树的高度
  • 6. 统计二叉树的指定层数的结点个数

前言

如果有看过的堆和堆排序这篇文章的话,你一定对二叉树的顺序存储有了一定的了解,但是这个是有特定的使用环境的。

那么在本文中,我将要给大家讲解二叉树的另一种表示方式——链式结构,以及我会给大家讲解有了顺序存储结构,为什么还要使用链式存储结构?以及如何用链式创建一棵二叉树?此外,还要讲解递归在二叉树中扮演着何种角色?

哈哈哈

1. 二叉树链式结构的意义

在学习堆时,我们说堆的本质就是一棵完全二叉树,而完全二叉树的结构注定了我们使用顺序表来存储比较方便,而这个就是二叉树的顺序存储。
二叉树的顺序存储
可能大家也意识到了一个点:好像二叉树的顺序存储有个要求,那就是这棵二叉树必须得是完全二叉树。没错!

如果我们不分青红皂白地使用顺序结构来存储二叉树会发生什么可怕的是事情呢?
请大家将目光往下注视:
非二叉树的顺序存储
可以看到,如果我们遇到的是一棵非完全二叉树,并且我们还要有顺序结构来存储这棵树。第一步,我们就得在逻辑上将这颗树补全为完全二叉树,但是补的地方我们是不能进行任何的操作的。那这个就十分尴尬,自己创建的空间却不能愉快地使用,有点扯淡了!而且这样的方式很浪费空间资源,从上图你就可以看出有几个空间是没有办法使用的。

那该怎么办呢?此时,天空一声巨响,二叉树的链式结构就闪亮登场了!

二叉树的链式结构针对的群体就是普通的二叉树。 那至于普通二叉树链式是如何被我们用代码创建的,让我们接着往下看!

2. 手搓一棵二叉树

这里为了降低大家的学习成本,我先不教大家如何用函数创建二叉树,如果大家对自己的二叉树比较有自信的话,可以移步到本文的后面,学习如何用函数创建一棵二叉树。

学习是要一步一步来的,我们不能不懂装懂,有时候也得直面不完美的自己!

二叉树链式结构示意图:
二叉链表

那我们这里开始就开始手搓一棵二叉树,为了方便大家学习,我会按照下面的图的给大家建立,往后的操作都是以这副图为基础。
图例
代码如下:

//二叉树结点的结构体
typedef int BTDataType;

typedef struct BTNode
{
	BTDataType data;
	struct BTNode* left;
	struct BTNode* right;

}BTNode;

BTNode* BuyNewnode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));

	newnode->data = x;

	newnode->left = NULL;
	newnode->right = NULL;


	return newnode;
}


BTNode* CreateTree()
{
	BTNode* node1 = BuyNewnode(1);
	BTNode* node2 = BuyNewnode(2);
	BTNode* node3 = BuyNewnode(3);
	BTNode* node4 = BuyNewnode(4);
	BTNode* node5 = BuyNewnode(5);
	BTNode* node6 = BuyNewnode(6);

	//开始结点之间的链接
	node1->left = node2;
	node1->right = node3;
	node2->left = node4;
	node2->right = node5;
	node3->right = node6;


	return node1;
}

这种方式初学者来说十分的友好。


3. 二叉树的遍历(重要)

接下来,我来讲一下本文的精华 —— “二叉树的遍历”。为什么会说是精华的部分呢?因为想要学会这个就得先知道递归的规则,并且这是科班同学在练习中会经常遇到的题目。同时这个也是二叉树的入门门槛!

二叉树遍历有四种方式,分别是:

  • 先序遍历(可能有的书籍会称其为"先根遍历")
  • 中序遍历
  • 后序遍历
  • 层序遍历

3.1 遍历的规则

  1. 先序遍历:遍历顺序为根、左子树、右子树。记忆的规则就是,先序又称先根,顾名思义就是根结点先遍历。
  2. 中序遍历:遍历顺序为左子树、根、右子树。记忆的规则就是,中序是将根结点放在遍历的中间顺序。
  3. 后序遍历:遍历顺序为左子树、右子树、根。记忆的规则就是,后序是将根节点但在遍历的最后顺序。
  4. 层序遍历:遍历的顺序时按照一层层开始,每一层从左往右依次遍历各节点。

在本文,会重点讲解先序遍历、中序遍历以及后序遍历。

为了更好的讲解每种遍历的规则,接下来我会分篇幅给大家做进一步的讲解。

3.2 先序遍历

先序遍历:遍历顺序为根、左子树、右子树

任何一颗树都是由根节点及其子树所构成的。二叉树也是如此,二叉树由其根节点、左子树和右子树所构成。其中,左子树由其根节点、左子树和右子树所构成…。给人一种循环的感觉,而这个就是递归!大家可以从下图(只做了部分的划分)感受一下
树的剖析图
好了,我们根据先序遍历的规则,写出它的数据遍历的顺序(注意:空树用NULL表示)。

先序遍历的顺序:1 2 4 NULL NULL 5 NULL NULL 3 NULL 6 NULL NULL

怎么样,不是很困难吧。

3.3 中序遍历

中序遍历:遍历顺序为左子树、根、右子树

趁热打铁,我们把中序遍历也讲了。

有了先序遍历的经验,中序遍历洒洒水啦!

树的剖析图

这里我就直接写答案了:

中序遍历的顺序:NULL 4 NULL 2 NULL 5 NULL 1 NULL 3 NULL 6 NULL

3.4 后序遍历

后序遍历:遍历顺序为左子树、右子树、根

树的剖析图

后序遍历的顺序:NULL NULL 4 NULL NULL 5 2 NULL NULL NULL 6 3 1

3.5 遍历的代码实现

这个就体现出递归的魅力时刻了!

这里我会拿先序遍历作为重点讲解,其余遍历方式的思维都是一样的。

3.5.1 先序遍历代码实现

先序遍历:遍历顺序为根、左子树、右子树

//先序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	printf("%d ",root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

当遇到一个递归却被深深绕进去时,最好画一个递归展开图!

递归展开图

3.5.2 中序遍历代码实现

中序遍历:遍历顺序为左子树、根、右子树

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%d ",root->data);
	InOrder(root->right);
}

3.5.3 后序遍历代码实现

后序遍历:遍历顺序为左子树、右子树、根

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

结果展示
可能大家在平常的练习中看到的是这种形式的,
结果
要想出来这个效果,我们只需要把printf("NULL ")这个语句注释掉即可。但是需要提醒大家的一点就是,上面这幅图的结果并不是二叉树原本的模样,大家要理解本质!

哈哈


4. 统计二叉树结点的个数

在这里我要给大家将一个非常重要的思想 —— “分治思想”

给大家设置一个场景,假如现在我是一位大学的校长,我要统计学校的人数。我该怎么做?

第一种方法:我到学生宿舍一个一个人的统计人数,每当宿舍有一个新生时,我就在我的小本本上画"正"字的一个笔画。最后我只要统计小本本上有多少个正字即可。

但是这个方法非常的费"校长"。不经的会感叹到这个校长当得也太累了吧!

第二种方法:既然身为一校之长,我肯定有一定的权力。我就让每个院的院长汇报各自院的人数给我,我再统计不就好了。院长接到任务后,就命令每个专业的系主任汇报各专业人数给我。每个系主任又叫每个班的班长统计各班的人数给他。之后,以宿舍为单位,让宿舍长汇报宿舍人数给班长,班长再做汇总,将结果给系主任。
这不就纯纯是打工人体系!!!

分治思想的体现
汇报人数的情况:
统计
有了这个思想之后,我们写代码!

int TreeSize(BTNode* root)
{
	//写法一
	if(root == NULL)
	{
		return 0;
	}
	
	return TreeSize(root->left) + TreeSize(root->right) + 1;

	/* //写法二
	return root == NULL? 0: TreeSize(root->left) + TreeSize(root->right) + 1;
	*/
}

结果


5. 统计二叉树的高度

用了分治思想,这个代码似乎也能理解了。

int TreeHeight(BTNode* root)
{
	if(root == NULL)
	{
		return 0; 
	}
	
	int left_height = TreeHeight(root->left);
	int right_height = TreeHeight(root->right);

	return left_height > right_height? left_height + 1:right_height + 1;
	
}

结果


6. 统计二叉树的指定层数的结点个数

这里给大家提供一个思路:当前K层结点的个数 = 对应左子树K-1层结点的个数 + 对应右子树K-1层结点的个数

int TreeKLevel(BTNode* root, int k)
{
	if(root == NULL)
	{
		return 0;
	}

	if(k == 1)
	{
		return 1;
	}
	
	int leftK = TreeKLevel(root->left,k-1);
	int rightK = TreeKLevel(root->right,k-1);
	
	return leftK + rightK;
}


好了,本文就到这里结束了!

本文主要讲解了二叉树链式结构的定义以及接口的实现,其中有个重要的思想可以帮助我们更快的理解递归背后的本质,体会到递归的魅力。

最后如果觉得本文写的不错的话,麻烦给偶点个赞吧!!!

请添加图片描述

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

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

相关文章

Python 函数用法与底层分析

在编写函数时,函数体中的代码写法和我们前面讲述的基本一致,只是对代码实现了封装,并增加了函数调用、传递参数、返回计算结果等内容。 函数简介函数(function)的基本概念 1:一个程序由一个一个的任务组成;函数就是代…

《CUDA编程》1.GPU硬件与CUDA环境搭建

1 GPU 介绍 GPU(graphics processing unit),意为图形处理器,也被称为显卡(graphics card)。GPU的浮点数运算峰值就比同时期的CPU高一个量级;GPU的内存带宽峰值也比同时期的CPU高一个量级。 CP…

【重学 MySQL】三十一、字符串函数

【重学 MySQL】三十一、字符串函数 函数名称用法描述ASCII(S)返回字符串S中的第一个字符的ASCII码值CHAR_LENGTH(s)返回字符串s的字符数,与CHARACTER_LENGTH(s)相同LENGTH(s)返回字符串s的字节数,和字符集有关CONCAT(s1,s2,…,sn)连接s1,s2,…,sn为一个字…

数据加密和数字证书

1 什么是数据加密 数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为"密文",使其只能在输入相应的密钥之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。 该过程的逆过程…

【开源免费】基于SpringBoot+Vue.JS服装商城系统(JAVA毕业设计)

本文项目编号 T 046 ,文末自助获取源码 \color{red}{T046,文末自助获取源码} T046,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

数据结构篇--折半查找【详解】

折半查找也叫做二分查找或者对数查找,是一种在有序数组中查找特定元素的查找算法。 折半查找的算法步骤如下: 将目标关键字key与数组中的中间元素比较,若相等则查找成功。key大于中间元素,就到数组中大于中间元素的部分进行查找&…

c++语法(模板初阶+类和对象部分知识点)

1:泛型编程 2:模板 2.1:函数模板 2.2:类模板 3:const成员函数与非const的区别 4:构造函数之初始化列表 4.1:初始化列表语法及其应用 4.2:explicit关键字 5:static成员变量,static成员函数 1:泛型编程 首先在提出泛型编程前我们先来看一下的代码(关于swap函数)。 void swap…

FPGA随记——VIVADO中ASYNC_REG指令

参考文章:Vivado综合属性系列一、ASYNC_REG_asyncregtrue-CSDN博客 -很棒棒的 跨时钟域设计(CDC)是个老生常谈的问题,其场景很多很杂,其中一个比较为人熟知的就是单bit信号从慢时钟到快时钟所采用的两级寄存器处理的…

抖音矩阵系统源码搭建短视频批量剪辑矩阵分发,可开源或oem

打造多语言短视频平台:技术实施方案揭秘 在短视频矩阵系统技术开发实施方案中,数据库设计是基础环节。首先,需要建立语言包数据库表并填充初始文本数据,如英语和中文的常用语。接着,要设计高效的数据库连接和数据访问接…

erlang学习:Linux命令学习3

shell基本输出 创建一个test.sh文件,并开放他的权限,之后向其中编辑以下内容 touch test.sh chmod 777 test.sh vim test.shecho "hello linux"之后运行相应shell程序得到输出 ./test.sh变量 单引号特点: 单引号里的任何字符都…

【Joint Receiver Design for ISAC】Neyman person | Gaussian | MMSE estimator |

【1】统计信号处理 Neyman-Pearson criterion pp 425 【1】 R c E { g x ( n ) x ( n ) H g H } σ 2 I g g H σ 2 I , \mathbf{R}_c\mathbf{E}\{\mathbf{g}x(n)x(n)^H\mathbf{g}^H\}\sigma^2\mathbf{I}\mathbf{g}\mathbf{g}^H\sigma^2\mathbf{I}, Rc​E{gx(n)x(n)HgH}σ2…

sheng的学习笔记-AI-归纳逻辑程序设计(ILP)

AI目录:sheng的学习笔记-AI目录-CSDN博客 规则学习(rule learning): sheng的学习笔记-AI-规则学习(rule learning)-CSDN博客 一阶规则学习: sheng的学习笔记-AI-FOIL(First-Order Inductive Learner)-CSD…

计算机组成原理(笔记4)

定点加减法运算 补码加法&#xff1a; 补码减法&#xff1a; 求补公式&#xff1a; 溢出的概念 在定点小数机器中,数的表示范围为|&#xff58;|<1。在运算过程中如出现大于1的现象,称为 “溢出”。 上溢&#xff1a;两个正数相加&#xff0c;结果大于机器所能表示的最…

全栈开发(一):springBoot3+mysql初始化

1.开发环境准备 1.开发工具 2.jdk下载 官网下载java17 3.java环境变量配置 用户变量&#xff1a; ①.JAVA_HOME ②.path 4.mysql下载 b站随便搜 5.新建项目 6.maven配置 可以下载zip放到目录里 这里是配置好的 repository文件夹&#xff1a;为maven提供下载的文件存放…

TSRPC+Cocos

TSRPC文档: https://tsrpc.cn/docs/get-started/api.html 创建 先创建一个默认的会话项目&#xff0c;找一个文件夹在控制台运行以下代码&#xff1a; npx create-tsrpc-applatest first-api --presets browser # 或者 yarn create tsrpc-app first-api --presets browser运…

SpringBoot3快速入门(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;JavaWeb关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ SpringBoot是什么 SpringBoot它可以帮我简单&#xff0c;快速地创建一个生产…

Java | Leetcode Java题解之第417题太平洋大西洋水流问题

题目&#xff1a; 题解&#xff1a; class Solution {static int[][] dirs {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};int[][] heights;int m, n;public List<List<Integer>> pacificAtlantic(int[][] heights) {this.heights heights;this.m heights.length;this.n…

实战19-详情页UI4等分

import { PADDING } from ../../constants/size; import rvp from ../../utils/resposive/rvIndex;Component export default struct SearchFilter {build() {Row() {Row({ space: rvp(6) }) {Text("位置").fontSize(rvp(14)).fontColor(#333333)Image($r(app.media.…

string map练习

to_string 在<string>头文件的std命名空间中 要格式化写浮点型入字符串用 2.map 直接尾插 set,map同方法&#xff0c;map只是把键变为pair键值对 4. string的花括号隐式类型转换不能字母个数字母&#xff0c;会被认为是初始化列表&#xff0c;而不是个数加字母的隐式类…

CSS简明通俗教程

CSS简明通俗教程 1. CSS简介 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种样式表语言。CSS用于描述HTML或XML&#xff08;包括SVG或XHTML等XML方言&#xff09;文档的呈现方式&#xff0c;它有自己的语法规则和逻辑&#xff0c;用于控制网…