【数据结构】二叉树篇

news2024/9/22 10:30:02

文章目录

  • 1.二叉树链式结构功能的实现
    • 1.1 前置说明
    • 1.2 二叉树的遍历
      • 1.2.1 前序、中序以及后序遍历
      • 1.2.2 层序遍历
    • 1.3 节点个数以及高度差
      • 1.3.1 二叉树的节点个数
      • 1.3.2 二叉树叶子节点个数
      • 1.3.3 二叉树第K层节点个数
      • 1.3.4 二叉树树查找值为x的节点
      • 1.3.5 二叉树的销毁
    • 1.4 代码整合

1.二叉树链式结构功能的实现

1.1 前置说明

在学习二叉树的基本操作前,需要先创建一颗二叉树,然后才能学习相关的基本操作,由于现在大家可能堆二叉树结构掌握还不够深入,为了降低大家的学习成本,本文会手动创建一颗简单的二叉树,目的快速进入二叉树的操作学习。后续可能就要在C++篇章详讲二叉树了。

创建一个结构体,该结构体就是二叉树节点,主要维护节点的数据和指向左子树和右子树的指针。
再写一个创建节点的函数就可以开始建造二叉树了。

#define DataType int

typedef struct BinaryTreeNode
{
	DataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//创建节点
BTNode* BuyNode(DataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = x;
	newnode->left = newnode->right = NULL;
	return newnode;
}

下面我会手动创建一个如下图的二叉树:
二叉树

void CreateTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	node1->left = node2;
	node1->right = node3;
	node2->left = node4;
	node2->right = node5;
	node3->left = node6;
}

注意:上述代码并不是创建二叉树的方式,正在创建二叉树的方式会再C++篇中再讲
在看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

  1. 空树
  2. 非空:根节点,根节点的左子树,根节点的右子树组成的。
    二叉树

从概念来看,二叉树的定义是递归式的因此后续遍历操作中基本都是按递归的方式来实现的。

1.2 二叉树的遍历

1.2.1 前序、中序以及后序遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树的遍历是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问节点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其他运算的基础。
按照规则,二叉树的遍历有:前序/中序/后序的递归遍历

  1. 前序遍历(先序遍历)-- 访问根节点的操作发生在遍历其左右子树之前访问顺序:根-左-右
  2. 中序遍历 – 访问根节点的存在在遍历左右子树之中访问顺序:左-根-右
  3. 后序遍历 – 访问根节点的操作在遍历其左右子树之后访问顺序:左-右-根

由于被访问的节点是某子树的根,所以N(Node),L(Left)和R(Right)又可以解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别被称为先根遍历,中根遍历,和后根遍历。

//二叉树的先序遍历
void PreOrder(BTNode* root);
//二叉树的中序遍历
void InOrder(BTNode* root);
//二叉树的后序遍历
void PostOrder(BTNode* root);

下面开始解析:
二叉树

注意N表示空节点的意思
前面我们讲到,先序遍历的顺序是根-左-右
提问:这里的根-左-右是什么意思呢?
回答:当我们经过一个节点时,先会取到该节点的数据,然后会进入该节点的左子树当左子树的数据全部取出后,再进入右子树去取数据。期间经过的所有节点都会重复这一套流程。
当我们利用这一思路开始遍历时,先画出前序遍历的结果
前序遍历

了解完思路后,代码其实非常简洁明了的,我们先确定一个递归出口,当节点为空时。该节点肯定没有左右子树了,那么就可以返回了,不过要注意的是,我这里打印了N,打印N的目的是为了让前序遍历更加清晰,其实也是可以不打印的。
确定完递归出口后,就是递归的主要逻辑,当我们取出当前节点的数据后,就要继续找当前节点的左子树的数据,为此就需要调用先序遍历,传当前节点的左指针。同理当左子树完毕后就是右子树了。

//二叉树的先序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}
//打印结果:
/*
1 2 4 N N 5 N N 3 6 N N N
*/

代码指向逻辑图:红色代表递进,绿色代表回归
代码运行图

中序遍历后后序遍历,其实都是类似的逻辑,大差不差的
中序遍历

//二叉树的中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
//打印结果:
/*
N 4 N 2 N 5 N 1 N 6 N 3 N
*/

中序遍历

后序遍历

//二叉树的后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
//打印结果
/*
N N 4 N N 5 2 N N 6 N 3 1
*/

后序遍历

1.2.2 层序遍历

层序遍历:除了先序遍历、中序遍历、后序遍历,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点然后从左到右访问第2层上的节点,接着是第三层节点,以此类推,自上而下,自左到右逐层访问树的节点的过程就是层序遍历。
明天补

1.3 节点个数以及高度差

下面将解释一些关于二叉树的常用功能

1.3.1 二叉树的节点个数

要数二叉树的节点个数,前面我们已经学会了二叉树的递归遍历,用到这里来就是了,这次我们不打印数据了,每经过一个节点就+1,然后进入该节点的左右子树。当遍历到空节点时就返回0,毕竟空节点可不算二叉树的节点个数的。

//二叉树的节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;//+1的位置随便放
}

1.3.2 二叉树叶子节点个数

要写出代码肯定要知道叶子节点的定义是什么,叶子节点就是没有度的节点。了解完这个,其实和上一个也没什么区别,无非就是把返回的节点从空变成了叶子节点,同时在返回的是还带来一个数据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);
}

1.3.3 二叉树第K层节点个数

别被什么第K层节点个数吓到了,其实也是很简单的。每次递进的时候就让k-1,当k==1时就说明到达了目标层数。不相信的话可以举几个例子。就比如第一层吧,刚进节点就成功匹配。

//二叉树第K层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1) + (k == 1);
}

1.3.4 二叉树树查找值为x的节点

返回的关键就是接收到的值是否为空,因为递归最终的时只会返回两种情况,一种就是找到了返回地址,一种就是返回NULL。了解完这两情况就很简单了,只有返回就是不是NULL就返回呗。

//二叉树树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, DataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* a = BinaryTreeFind(root->left, x);
	BTNode* b = BinaryTreeFind(root->right, x);
	if (a!=NULL||b!=NULL )
	{
		if (a != NULL)
			return a;
		else
			return b;
	}
	return NULL;
}

1.3.5 二叉树的销毁

销毁要用后序遍历,可不能先释放。

//二叉树的销毁
void DestoryTree(BTNode* root)
{
	if (root == NULL)
		return;
	DestoryTree(root->left);
	DestoryTree(root->right);
	free(root);
}

1.4 代码整合

//tree.h
#include <stdio.h>
#include <stdlib.h>

#define DataType int

typedef struct BinaryTreeNode
{
	DataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//创建节点
BTNode* BuyNode(DataType x);

//二叉树的先序遍历
void PreOrder(BTNode* root);

//二叉树的中序遍历
void InOrder(BTNode* root);

//二叉树的后序遍历
void PostOrder(BTNode* root);

//二叉树的节点个数
int BinaryTreeSize(BTNode* root);

//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);

//二叉树第K层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

//二叉树树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, DataType x);

//二叉树的销毁
void DestoryTree(BTNode* root);


//tree.c
#include "tree.h"

//创建节点
BTNode* BuyNode(DataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = x;
	newnode->left = newnode->right = NULL;
	return newnode;
}

//二叉树的先序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

//二叉树的中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

//二叉树的后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

//二叉树的节点个数
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层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1) + (k == 1);
}

//二叉树树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, DataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* a = BinaryTreeFind(root->left, x);
	BTNode* b = BinaryTreeFind(root->right, x);
	if (a!=NULL||b!=NULL )
	{
		if (a != NULL)
			return a;
		else
			return b;
	}
}

//二叉树的销毁
void DestoryTree(BTNode* root)
{
	if (root == NULL)
		return;
	DestoryTree(root->left);
	DestoryTree(root->right);
	free(root);
}

//test.c
#include "tree.h"

void CreateTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(6);
	node1->left = node2;
	node1->right = node3;
	node2->left = node4;
	node2->right = node5;
	node3->left = node6;
	node3->right = node7;
	//int n = BinaryTreeSize(node1);
	//int n = BinaryTreeLeafSize(node1);
	//int n = BinaryTreeLevelKSize(node1,3);
	//printf("%d", n);
	BTNode* tmp = BinaryTreeFind(node1, 1);
}

int main()
{
	CreateTree();
	return 0;
}

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

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

相关文章

RAG 工具和框架介绍: Haystack、 LangChain 和 LlamaIndex

Haystack、 LangChain 和 LlamaIndex&#xff0c;以及这些工具是如何让我们轻松地构建 RAG 应用程序的&#xff1f; 我们将重点关注以下内容&#xff1a; HaystackLangChainLlamaIndex 增强LLM 那么&#xff0c;为什么会有这些工具存在呢&#xff1f;如你所知&#xff0c;C…

LVS学习与练习

LVS (Linux Virtual Server) 是一种高性能的负载均衡解决方案&#xff0c;它基于 Linux 内核实现。LVS 可以用来构建高可用性和高性能的 Web 服务器集群。LVS 支持多种负载均衡算法和模式&#xff0c;可以有效地分发网络请求到多台后端服务器上。 LVS 的主要组成部分 1. Direc…

InternLM+LlamaIndex RAG 实践

本期实战训练营介绍了使用书生葡语的InternLM和LlamaIndex框架进行RAG项目的实践。内容分为三个部分&#xff1a;回顾InternLM的发展历程&#xff0c;介绍RAG的基本概念和应用&#xff0c;以及实践一个RAG项目。RAG技术结合了检索与生成&#xff0c;通过外部知识库增强大模型的…

开源Docker图形化管理工具DockerUI

DockerUI 是一个 Web 用户界面&#xff0c;它允许用户通过浏览器与 Docker 守护进程进行交互&#xff0c;而无需在命令行中执行 Docker 命令。它为 Docker 容器、镜像、网络等提供了直观的图形界面管理。然而&#xff0c;需要注意的是&#xff0c;DockerUI 已经不再是最流行的 …

领先数年!这款ERP系统已成制造业趋势!

企业数字化转型的趋势不可阻挡&#xff0c;在全球化竞争的背景下&#xff0c;市场变化迅速&#xff0c;客户需求日益多样化&#xff0c;企业需要更加敏捷地响应市场变化&#xff0c;提高生产效率和产品质量&#xff0c;以满足客户的需求。 而在这一转型过程中&#xff0c;ERP …

怎么直接在PDF上修改内容?随心编辑PDF内容

PDF(Portable Document Format)作为一种专用于阅读而非编辑的文档格式&#xff0c;其设计的核心目的是保持文档格式的一致性&#xff0c;确保文档在不同平台和设备上都能以相同的布局和格式呈现。然而&#xff0c;在实际工作和生活中&#xff0c;我们经常需要对PDF文档进行编辑…

Python编写Word文档

目录 1. 创建word文档 2. 添加标题、居中、字体16大小 3. 添加标题一 4. 添加一段话并设置字体颜色 5. 换页 6. 插入表格 1. 创建word文档 from docx import Documentdoc Document() 2. 添加标题、居中、字体16大小 from docx.shared import Pt from docx.enum.text i…

AT360-6T杭州中科微授时模块场景应用

AT360-6T是一款高性能多系统卫星定位授时模块&#xff0c;基于自主研发的北斗多系统 SOC 芯片&#xff0c;可以同时接收中国的 BDS(北斗二号和北斗三号)、美国的 GPS、俄罗斯的 GLONASS、欧盟的 GALILEO 和日本的 QZSS 等多个卫星导航系统的 GNSS 信号来实现多系统联合定位授时…

实用篇 | 服务器查看监听端口的程序

对于一些程序员最痛苦的是接手一些“二手系统“&#xff0c; 由于年久失修&#xff0c; 加上裁员离职&#xff0c;系统文档不完善等原因&#xff0c; 只留下服务器配置和代码。 接手人&#xff0c;只能对着这些仅存的代码和服务器硬刚&#xff0c; 对服务器硬刚的第一步&#x…

高盛推荐包装食品行业两大首选股票

市场动荡与食品行业的防御性 近期市场的剧烈波动、7月份疲弱的就业报告以及对美联储可能降息的预期&#xff0c;引发了人们对经济衰退的担忧。尽管市场在本周有所反弹&#xff0c;投资者们开始调整策略&#xff0c;寻求更具防御性的投资方向。在这种背景下&#xff0c;包装食品…

Vue2移动端(H5项目)项目基于vant实现select单选(支持搜索、回显、自定义下拉label展示功能)

一 最终效果 二、参数配置 1、代码示例&#xff1a; <t-selectv-model"formData.materialNo"valueKey"materialNo"showLabel"materialName"labelKey"label"label"判定品级"input-align"right"placeholder&qu…

Google Earth Engine(GEE)——1986-2021年黄河入海口区域的逐年影像展示案例分析,并加载可以分享的URL链接

函数: size() Returns the number of elements in the collection. 返回集合中元素的数量。 Arguments: this:collection (FeatureCollection): The collection to count. Returns: Integer 融合影像可以一个接一个进行融合 merge(collection2) Merges two image co…

CAXA快捷键

1.左下角处选择导航&#xff0c;快捷键F6可以切换 2.自定义快捷键 鼠标右键点击菜单栏空白处 点击自定义&#xff0c;便可以修改快捷键

Leetcode—1239. 串联字符串的最大长度【中等】(unordered_set)

2024每日刷题&#xff08;155&#xff09; Leetcode—1239. 串联字符串的最大长度 实现代码 class Solution { public:bool charSet(string & s) {unordered_set<char> charSet(s.begin(), s.end());// true表示有重复// false表示唯一return s.size() ! charSet.s…

HTML原生手搓询盘

效果展示&#xff1a; 1、PC布局&#xff1a; 2、移动布局&#xff1a; 3、交互展示&#xff1a; 1、HTML代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…

鸿蒙学习(一):基础知识

认识和存储数据 基础类型&#xff1a; 数字&#xff1a;number 布尔值&#xff1a;boolean 变量存储&#xff1a; let 变量名&#xff1a;类型 值 let title:string 你好 注意&#xff1a;1、字符串需要引号引起来&#xff1b;2、单引号双引号都可以&#xff1b;3、存储的内…

高校如何利用AIGC提高教学效率?

AIGC技术可按照模态分为文本、图像、语言以及多模态等&#xff0c;其中以图像发展最为迅速&#xff0c;AI绘画技术深入广告设计、营销、工业设计、游戏设计等各个领域&#xff0c;复合增长率将超过80% &#xff0c;未来以内容生产模式变革为根本将引爆生产力革命。目前AI人才缺…

HTML实现弹出层

leopard/ˈlepərd/ 豹子&#xff0c;豹纹 弹出层指的是鼠标悬停于某个元素之上时显示的一个界面组件。 关注和理解特性&#xff1a;z-index属性和动态生成HTML元素。 HTML5新增&#xff1a; figure:媒体内容(图像&#xff0c;音频&#xff0c;视频)&#xff0c;用于包含一…

uniapp本地打包app安装说明

uniapp本地打包app安装说明 目录 uniapp本地打包app安装说明一、打包说明1.HBuilder X 生成本地打包资源2.Android Studio和App离线SDK环境准备2.1 下载Android Studio和 App离线SDK2.2 资源替换2.3 id属性值修改。2.4 添加provider信息到AndroidManifest.xml中的<applicati…

人工智能领域颠覆性技术创新,数字人泛化AI时代来临

是先有鸡还是先有蛋&#xff0c;这个问题人类还没有搞清楚&#xff0c;这次又有一个新的问题产生了&#xff0c;是算法进化了AI&#xff0c;还是AI进化了算法。我们知道直播平台都是利用算法对数字人直播进行斟别&#xff0c;但这一次被数字人泛化技术颠覆了&#xff0c;AI回复…