【数据结构】第十一站:链式二叉树

news2025/1/11 22:56:20

目录

一、二叉树的创建

二、二叉树的遍历

1.前序中序后序遍历

2.层序遍历

三、二叉树的节点个数以及高度等

四、二叉树的构建和销毁

五、DFS和BFS


一、二叉树的创建

为了方便后面的讨论,我们在这里先手撕一颗二叉树

typedef int BTDateType;
typedef struct BinaryTreeNode
{
	BTDateType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//生成一个二叉树节点
BTNode* BuyNode(BTDateType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;

	return newnode;
}

//生成一颗二叉树
BTNode* CreatBinaryTree()
{
	BTNode* n1 = BuyNode(1);
	BTNode* n2 = BuyNode(2);
	BTNode* n3 = BuyNode(3);
	BTNode* n4 = BuyNode(4);
	BTNode* n5 = BuyNode(5);
	BTNode* n6 = BuyNode(6);

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;

	return n1;
}
注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后文讲解
二叉树是:
1. 空树
2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

二、二叉树的遍历

1.前序中序后序遍历

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

按照规则,二叉树的遍历有: 前序 / 中序 / 后序的递归结构遍历
1. 前序遍历 (Preorder Traversal 亦称先序遍历 )—— 访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历 (Inorder Traversal)—— 访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历 (Postorder Traversal)—— 访问根结点的操作发生在遍历其左右子树之后。
由于被访问的结点必是某子树的根, 所以 N(Node )、 L(Left subtree )和 R(Right subtree )又可解释为 根、根的左子树和根的右子树 NLR LNR LRN 分别又称为先根遍历、中根遍历和后根遍历。
如下图所示,是二叉树的先序中序后序的图解

二叉树的先序中序后序代码为如下所示

//二叉树的先序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}
//二叉树的中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
//二叉树的后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	PostOrder(root);
	printf("\n");
	return 0;
}

2.层序遍历

二叉树的层序遍历值得是一层一层的将数据给遍历下来。

为了一层一层的遍历,我们必须得借助队列才能实现。我们的思想是这样的,一开始先让根节点入队列。然后当队列不为空的时候,将队头出队列,然后将二叉树的左孩子和右孩子入队列。然后打印队头的数据。这样一直循环下去,每出一个节点,都需要入两个孩子。当然如果孩子为空,就没有必要入队列了。直到将队列出光,正好也就打印完了。

这里我们需要注意的是,当我们的队列代码复制到二叉树中的时候,我们需要将队中数据的类型修改为二叉树结点指针类型。

 

//二叉树的层序遍历
void LevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);
}

三、二叉树的节点个数以及高度等

1.二叉树的计算节点个数

//二叉树的节点个数
void TreeSize1(BTNode* root, int* psize)
{
	if (root == NULL)
	{
		return;
	}
	(*psize)++;
	TreeSize1(root->left, psize);
	TreeSize1(root->right, psize);
}
int TreeSize2(BTNode* root)
{
	return root == NULL ? 0 : 1 + TreeSize2(root->left) + TreeSize2(root->right);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	PostOrder(root);
	printf("\n");

	int size1 = 0;
	TreeSize1(root, &size1);
	printf("TreeSize1:%d\n", size1);
	int size2 = 0;
	TreeSize1(root, &size2);
	printf("TreeSize1:%d\n", size2);

	printf("TreeSize2:%d\n", TreeSize2(root));
	return 0;
}

如上代码所示,是二叉树的节点个数的计算,有两种方式可以实现,一种是使用一个指针,然后传参,将数都累加在指针所指向的变量上。

另外一种是采用分治的思想,分而治之,这颗树的结点个数等于 1+左子树+右子树

2.二叉树的高度

//二叉树的高度
int TreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int LeftHeight = TreeHeight(root->left);
	int RightHeight = TreeHeight(root->right);
	int ChildHeight = LeftHeight > RightHeight ? LeftHeight : RightHeight;
	return 1 + ChildHeight;
}

如上代码所示,我们仍然采用双路递归,如果根节点为空,那么高度为0,否则计算左子树和右子树的中最大的那个高度,然后加1返回即可

3.二叉树第k层的节点个数

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

如上代码所示,这道题的关键核心是,根节点的第k层的节点个数可以转化为左子树第k-1层的节点个数加上右子树第k-1层的节点个数。还有一点就是递归的结束条件,如果树为空,返回0,如果为第一层,则返回1即可

4.二叉树查找一个值为x的节点

//二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDateType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	BTNode* left = TreeFind(root->left, x);
	if (left != NULL)
	{
		return left;
	}
	BTNode* right = TreeFind(root->right, x);
	if (right != NULL)
	{
		return right;
	}
	return NULL;
}

我们先判断这颗树是否为空,如果为空,那么直接返回NULL,然后再判断当前节点是否为x,如果是,返回即可。最后我们再找一下左子树,然后找一下右子树,如果都没有找到,那就返回NULL

5.二叉树的叶子结点个数

//二叉树叶子节点个数
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left || root->right)
	{
		return TreeLeafSize(root->left) + TreeLeafSize(root->right);
	}
	else
	{
		return 1;
	}
}

如上代码所示,我们先判断根节点,如果根节点为空,直接返回0,如果左孩子和右孩子只要有一个不为空,那么就计算左子树和右子树的结点个数。如果左右孩子都为空,那么返回1

四、二叉树的构建和销毁

1.二叉树的构建

这里我们使用这道题来完成这个接口:二叉树遍历_牛客题霸_牛客网

#include <stdio.h>
#include<stdlib.h>
typedef struct TreeNode
{
    struct TreeNode* left;
    struct TreeNode* right;
    char val;
}TreeNode;
TreeNode* CreatTree(char* a,int* pi)
{
    if(a[*pi]=='#')
    {
        (*pi)++;
        return NULL;
    }
    TreeNode* root=(TreeNode*)malloc(sizeof(TreeNode));
    root->val=a[(*pi)++];
    root->left=CreatTree(a,pi);
    root->right=CreatTree(a,pi);
    
    return root;
}
void InOrder(TreeNode* root)
{
    if(root==NULL)
    {
        return;
    }
    InOrder(root->left);
    printf("%c ",root->val);
    InOrder(root->right);
}
int main() {
    char a[101]={0};
    scanf("%s",a);
    int i=0;
    TreeNode* root=CreatTree(a,&i);
    InOrder(root);
    return 0;
}

这个接口也不难理解,我们根据先判断当前的字符是否为'#',如果是则返回NULL,否则就创建好一个节点,然后开始递归创建左子树和右子树

2.判断一棵树是否为完全二叉树

我们可以利用层序的思路,完全二叉树的特点就是,层序遍历出现第一个NULL的时候,以后都是NULL了,所以我们这样做,当第一次出现NULL的时候,我们先跳出这个循环,然后从后面开始不断出队判断是否存在不为空的结点。一旦存在直接返回false。

//判断一颗二叉树是否为完全二叉树
bool TreeComplete(BTNode* root)
{
	if (root == NULL)
		return true;
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			break;
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

3.二叉树的销毁

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

对于二叉树的销毁,我们需要注意的是最好使用后序遍历销毁,如果采用前序中序的话,我们就必须得创建临时变量了,这样就非常繁琐。

五、DFS和BFS

1.DFS:深度优先遍历

对于二叉树而言,他的深度优先遍历其实就是先序遍历,当然如果不是很严格的话中序和后序也算深度优先遍历。因为中序和后序的递归过程与先序是一样的,只不过是根节点的时机不一样

 但是要注意,不能说先序遍历就是深度优先遍历,因为深度优先遍历还包括图,二维数组等的遍历。只能说二叉树的前序遍历是深度优先遍历或二叉树的深度优先遍历是前序遍历

2.BFS:广度优先遍历

对于二叉树而言,他的广度优先遍历就是层序遍历

 


本节内容到此位置

如果对你有帮助的话,不要忘记点赞加收藏哦!!!

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

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

相关文章

Qt Designer

Qt Designer——即Qt设计师&#xff0c;是QT项目开发的可视化图形界面编辑器&#xff0c;通过设计师可以很方便地创建图像界面文件*.ui&#xff0c;然后将ui文件应用的源代码中&#xff0c;做到所见即所得&#xff0c;让界面的设计变得十分简单。下面介绍Qt Designer的简单使用…

使用PowerShell自动部署ASP.NetCore程序到IIS

asp.net core 安装asp.net core sdk https://dotnet.microsoft.com/en-us/download/dotnet/3.1 创建asp.net core项目 dotnet new webapi运行项目 访问https://localhost:5001/WeatherForecast iis配置 安装iis 以管理员身份运行powershell Enable-WindowsOptiona…

【计算机网络】学习笔记:第二章 物理层(五千字详细配图)【王道考研】

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

Windows中Seata连接Linux中的nacos的配置

目录 前言&#xff1a; Seata配置教程&#xff1a; file.conf: ​编辑 配置代码&#xff1a; registry.conf: ​编辑 nacos-config.sh: config.txt: 前言&#xff1a; 许多博主&#xff0c;把两个软件都安装在同一个系统中&#xff0c;但是有些人估计是装在不同的环境下…

支付系统设计:收银台设计二

文章目录 前言1. 接口校验1.1 Chains1.2 Checker1.2.1 AbstractChecker1.2.2 TokenChecker1.2.3 OrderChecker1.2.4 UserInfoChecker1.2.5 BaseInfoChecker1.2.6 SignChecker 1.3 ApiFilter 2. 下单3. 收银台首页2.1 OrderInfoResolver2.2 UserBaseInfoResolver 4. 执行流程总结…

企业宁愿花 15K 重新招人,也不愿意花 10K 留住老测试员?

金三银四即将进入尾声&#xff0c;大家逐渐收敛了跳槽涨薪的想法&#xff0c;准备收收心等待过年。不置可否&#xff0c;年后必定又是一波跳槽季&#xff0c;通过跳槽才能涨薪已经成为了不少求职者内心默认的定理。 不知道什么时候开始&#xff0c;公司不能满足加薪的要求&…

leetcode 812. 最大三角形面积

题目 给你一个由 X-Y 平面上的点组成的数组 points &#xff0c;其中 points[i] [xi, yi] 。从其中取任意三个不同的点组成三角形&#xff0c;返回能组成的最大三角形的面积。与真实值误差在 10-5 内的答案将会视为正确答案。 示例 1&#xff1a; 输入&#xff1a;points [[…

手把手带你实现通讯录C语言

通讯录大家都很熟悉了&#xff0c;一个联系人包括姓名&#xff0c;年龄&#xff0c;性别&#xff0c;电话&#xff0c;地址等&#xff1b; 那我们想一想我们所学的数据类型&#xff08;int,float,double等都是单一的相同类型 &#xff08;属于内置类型&#xff09;&#xff09;…

#详细介绍!!! 造成死锁的原因以及解决方案!

本篇主要是介绍什么是死锁&#xff0c;已经死锁产生的原因&#xff0c;如果避免死锁。根据上述的几个问题让我们来阅读本篇文章。 目录 1. 什么是死锁 2. 形成死锁的原因&#xff08;四个必要条件&#xff09; 3. 如果有效避免死锁 1. 什么是死锁 死锁主要是锁彼此间进行锁等…

FPGA实现JPEG-LS图像压缩,有损无损可配置,提供工程源码和技术支持

目录 1、前言2、JPEG-LS图像压缩理论3、JPEG-LS图像压缩性能介绍4、JPEG-LS图像压缩时序介绍5、JPEG-LS图像压缩输出压缩流6、工程源码和仿真7、福利&#xff1a;工程代码的获取 1、前言 JPEG-LS&#xff08;简称JLS&#xff09;是一种无损/有损的图像压缩算法&#xff0c;其无…

1 Go语言开发环境搭建详细教程【Go语言教程】

Go语言开发环境搭建【Win、Linux、Mac】 1 SDK下载 官网地址&#xff1a;golang.org,因为一些原因国内可能无法访问。可以使用下面第二个链接。国内地址访问&#xff1a;https://golang.google.cn/dl或者https://www.golangtc.com/download 根据自己操作系统版本&#xff0c;下…

30天学会《Streamlit》(9)

30天学会《Streamlit》是一项编码挑战&#xff0c;旨在帮助您开始构建Streamlit应用程序。特别是&#xff0c;您将能够&#xff1a; 为构建Streamlit应用程序设置编码环境 构建您的第一个Streamlit应用程序 了解用于Streamlit应用程序的所有很棒的输入/输出小部件 第9天 - …

超高精度PID控制器的特殊功能(4)——分程控制功能及其应用

摘要&#xff1a;分程控制作为一种典型的复杂控制方法之一&#xff0c;常用于聚合反应工艺、冷热循环浴、TEC半导体温度控制、动态平衡法的真空和压力控制等领域。为快速和便捷的使用分程控制&#xff0c;避免采用PLC时存在的控制精度差和使用门槛高等问题&#xff0c;本文介绍…

pyqt6+vtk

这里用PyQt6vtk9.2.6 pip install PyQt6 pyqt6-tools vtk这里拉了一个水平布局 然后水平布局中加入QWidget&#xff0c;object name改为vtkWidget 右键Promote Widgets Promoted class name: QVTKRenderWindowInteractor Header file: vtkmodules.qt.QVTKRenderWindowInterac…

文章改写神器在线-AI续写文章生成器

AI续写生成器 AI续写生成器是一种利用人工智能技术的创意工具&#xff0c;能够提高写作效率&#xff0c;为营销推广带来全新的可能性。无论你是写手、广告人员还是市场营销人员&#xff0c;这个工具都能够有效地解决你在写作中遇到的难题。 在内容创作行业中&#xff0c;原创…

Amazon Linux2部署安装Jenkins

先决条件 服务器配置要求 256 MB of RAM 1 GB of drive space (although 10 GB is a recommended minimum if running Jenkins as a Docker container) 需要部署安装JDK环境部署安装的Jenkins版本为Version 2.400 部署安装JDK 1. 下载JDK软件包 wget https://corretto.aws/…

Altium Designer借助嘉立创添加PCB封装和3D模型

目录 引言打开立创专业版EDA&#xff0c;建立项目从立创商城找到器件编码添加PCB封装导出和修改3D封装 引言 由于使用Altium Designer的频率并不是特别高&#xff0c;所以每一次使用总是得东跌西撞的才回忆起一些使用步骤。因此&#xff0c;想在这里记录一下Altium Designer借…

【mysql】binlog日志

目录 1.1 基本说明1.2 binlog日志格式1.3 binlog日志查看1.4 binlog日志删除1.5 binlog操作示例 1.1 基本说明 1.全称binary log&#xff0c;二进制日志 2.记录了所有的DDL语句&#xff08;Data Definition Language数据定义语言&#xff09;和DML语句&#xff08;Data Manipul…

Java多线程中sleep()方法和wait()方法的区别

目录 具体而言 &#xff0c;sleep&#xff08;&#xff09;方法与wait&#xff08;&#xff09;方法的区别主要表现在以下几个方面&#xff1a; 引申&#xff1a;sleep&#xff08;&#xff09;方法和yield&#xff08;&#xff09;方法有什么区别&#xff1f; 常见面试题&a…