二叉树——链式存储

news2024/9/28 13:21:41

 ✅<1>主页:我的代码爱吃辣
📃<2>知识讲解:数据结构——二叉树
🔥<3>创作者:我的代码爱吃辣
☂️<4>开发环境:Visual Studio 2022
💬<5>前言:上期讲了二叉树的顺序存储,今天来讲一下二叉树的链式存储。

目录

一.结点创建:

二.二叉树的遍历:

(1)前序,中序,以及后序遍历

 2.中序遍历

3.后序遍历

二.二叉树的层序遍历

 三.二叉树节点数

 四. 叶子结点数

 五.最大高度

六.查找二叉树结点

七.二叉树的销毁

八.二叉树的创建


在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

一.结点创建:

typedef int BTDatetype;
//二叉树结点
typedef struct TreeNode
{
	int val;
	struct TreeNode* left;
	struct TreeNode* right;
}BTNode;

//结点的创建
BTNode* BTBuyNode(int val)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	node->val = val;
	node->right = node->left = NULL;
	return node;
}
int main()
{
	BTNode* n1 = BTBuyNode(1);
	BTNode* n2 = BTBuyNode(2);
	BTNode* n3 = BTBuyNode(3);
	BTNode* n4 = BTBuyNode(4);
	BTNode* n5 = BTBuyNode(5);
	BTNode* n6 = BTBuyNode(6);
	BTNode* n7 = BTBuyNode(7);
	n1->left = n2;
	n1->right = n3;
	n2->left = n4;
	n2->right = n5;
	n3->left = n6;
	n3->right = n7;
	
}

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

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

从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

二.二叉树的遍历:

(1)前序,中序,以及后序遍历

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

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。 

1.先序遍历:

访问根结点的操作发生在遍历其左右子树之前,意思就是在每一颗树而言都是,先访问根,在访问左子树,最后访问右子树。

 A作为整个树的根,先访问A,紧接着访问左子树,面对左子树也是由根左右构成,所以再访问左子树的根B,在访问B的左子树,以此类推经行递归。

代码:

//前序遍历
void PrevOrder(BTNode* root)
{
    //如果这棵树是空树,就直接返回
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
    //如果这棵树不是空树,那就先访问根,再递归访问左子树和右子树
	printf("%d ", root->val);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

测试:

int main()
{    
    //二叉树创建
	BTNode* n1 = BTBuyNode(1);
	BTNode* n2 = BTBuyNode(2);
	BTNode* n3 = BTBuyNode(3);
	BTNode* n4 = BTBuyNode(4);
	BTNode* n5 = BTBuyNode(5);
	BTNode* n6 = BTBuyNode(6);
	BTNode* n7 = BTBuyNode(7);
	n1->left = n2;
	n1->right = n3;
	n2->left = n4;
	n2->right = n5;
	n3->left = n6;
	n3->right = n7;
    //前序遍历
	PrevOrder(n1);
	return 0;
}

 2.中序遍历

中序遍历,在递归思想上和前序遍历一样,唯一的区别就是,前序遍历时先访问根节点,再访问左右子树,而中序遍历是:先访问左子树再访问根节点最后在访问右子树。

代码:

void MidOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	MidOrder(root->left);
	printf("%d ", root->val);
	MidOrder(root->right);
}

3.后序遍历

先访问左子树、再访问右子树,最后访问根结点。

代码:

void BankOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BankOrder(root->left);
	BankOrder(root->right);
	printf("%d ", root->val);
}

 

二.二叉树的层序遍历

二叉树的层序遍历,就是从上往下,从左往右,一个一个遍历。

例如:

 要想达成这样的遍历,我们需要借助队列,主要的操作就是,在结点出队列时,如果他有孩子就将该结点的孩子进队列,一直出队列直到队列为空。

代码:

//层序遍历
void LevelOrder1(BTNode* root)
{

	Queue Q; //创建队列
	QueueInit(&Q); //初始化队列
	QueuePush(&Q, root); //将第一个结点的指针入队
	//循环出队,直到队空
	while (!QueueEmpty(&Q))
	{
		
		BTNode* tmp = QueueFront(&Q);//出队头数据
		QueuePop(&Q);//删除队头数据
		printf("%d ", tmp->val);
		//如果出队结点的左孩子不为空就进队
		if (tmp->left)
		{
			QueuePush(&Q, tmp->left);
		}
		//如果出队结点的右孩子不为空就进队
		if (tmp->right)
		{
			QueuePush(&Q, tmp->right);
		}
	}
	QueueDestroy(&Q);
}

 三.二叉树节点数

我们利用二叉树的结构特点,利用递归思想来计算二叉树的结点个数,一个二叉树我们看成由根节点,左子树,右子树,那么一颗二叉树的结点个数,就是根节点数,加上左子树结点个数,加上右子树节点个数。

代码:

//节点数
int SizeNode(BTNode* root)
{
    //如果是空树,没有一个结点,那就返回0
	if (root == NULL)
	{
		return 0;
	}
	int Lsize = SizeNode(root->left);  //左子树结点个数
	int Rsize = SizeNode(root->right); //右子树结点个数
	return Lsize + Rsize + 1;  //根+左+右
}

 四. 叶子结点数

首先我们要知道,叶子结点的特点,叶子节点没有左右孩子,或者左右孩子都是空。这样的结点才能是叶子节点。我们利用递归的思想怎么解释这个问题呢?我们只需要左子树的叶子数加上右子树的叶子数。

代码:

//叶子数
int LeafSize(BTNode* root)
{    
    //空树没有结点返回0
	if (root == NULL)
	{
		return 0;
	}
    //如果是叶子,就返回1
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	int LLeafsize = LeafSize(root->left);   //左子树叶子节点数
	int RLeafsize = LeafSize(root->right);  //右子树叶子节点数
	return LLeafsize + RLeafsize;  
}

 

 五.最大高度

二叉树的最大深度,利用递归的思想来解释,可以看成左右子树较大的高度加上一个根节点,就是这棵树的做大高度。

代码:

int BTdeep(BTNode*root)
{
    //空树高度为0
	if (root == NULL)
	{
		return 0;
	}
	int Ldeep = BTdeep(root->left);  //左子树高度
	int Rdeep = BTdeep(root->right); //右子树高度
	return (Ldeep > Rdeep? Ldeep : Rdeep) + 1; //左右子树较高的一棵树高度加上一
}

六.查找二叉树结点

利用递归的思想,查找二叉树结点并返回出来,找不到返回NULL。我们先判断根节点是不是我们查找的结点,如果根节点不是,再查找左子树和右子树是否有目标结点。如果左右子树都没有,根节点又不是,就返回NULL。

代码:

//查找节点
BTNode* FindNode(BTNode* root, int x)
{
	//空树没有一个结点
	if (root == NULL)
		return NULL;
	//如果根节点是我们的目标结点,就直接返回
	if (root->val == x)
		return root;
	//查找左子树
	BTNode* Lfind = FindNode(root->left, x);
	if (Lfind)//如果左子树找到的不是空,就代表找到了
		return Lfind;
	//查找右子树
	BTNode* Rfind = FindNode(root->right, x);
	if (Rfind)//如果右子树找到的不是空,就代表找到了
		return Rfind;

	return NULL;
}

测试:

 

七.二叉树的销毁

销毁一颗二叉树,我们应该借助,后序遍历的思想,最后销毁根节点,不然我们提前销毁根节点了,就会导致无法销毁另一颗子树。

代码:

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

八.二叉树的创建

牛客——二叉树的创建和遍历

 思路:我们利用先序遍历的思路,遍历数组的同时,判断是否是有效的结点,先创建根,在创建左子树,最后创建右子树,最后返回根结点。

#include <stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>

typedef char BTDataType;

typedef struct BTNode
{
	BTDataType val;
	struct BTNode* Lchild;
	struct BTNode* Rchild;
}BTNode;
BTNode* BinaryTreeCreate(BTDataType* a,int* pi)
{
    
	if(a[*pi]=='#')
    {
        (*pi)++;
        return NULL;
    }
    //创建根节点
    BTNode*root=(BTNode*)malloc(sizeof(BTNode));
    root->val=a[(*pi)++];
    //创建左子树
    root->Lchild=BinaryTreeCreate(a, pi);
    //创建右子树
    root->Rchild=BinaryTreeCreate(a, pi);
    return root;
}

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	
	PrevOrder(root->Lchild);
	printf("%c ", root->val);
	PrevOrder(root->Rchild);
}
int main() {

    char arr[101];
    scanf("%s",arr);
    int i=0;
    BTNode*root1=BinaryTreeCreate(arr, &i);
    PrevOrder(root1);
    return 0;
}

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

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

相关文章

LinkedList与链表(一)(非循环单向链表)

ArrayList的缺陷通过ArrayList上节课的学习&#xff0c;我们了解到如果ArrayList要删除或插入一个元素&#xff0c;后面的元素都要进行移动&#xff0c;时间复杂度为O(n),效率比较低&#xff0c;因此ArrayList不适合做任意位置的插入和删除操作比较多的场景。因此java集合又引入…

Python if __name__ == “__main__“ 用法

文章目录1 前言2 原理3 __name__变量的作用参考1 前言 在很多Python程序中&#xff0c;我们都会遇到if __name__ "__main__"的情况&#xff0c;却不知道为何要这样做 在很多编程语言中&#xff0c;如C、Java等&#xff0c;都需要程序的入口&#xff0c;一般都是ma…

MySql锁机制(全网最全、最详细、最清晰)

1、MySql锁机制 锁机制的作用&#xff1a; 解决因为资源共享&#xff0c;而造成的并发问题。 没有锁机制时&#xff1a; 例如一号用户和二号用户都要去买同一件商品&#xff08;假如这件商品是一件衣服&#xff09;&#xff0c;一号用户手速稍微快了一些&#xff0c;于是就…

从事软件测试需要学自动化么

相信许多对软件测试有过一点了解的人&#xff0c;对自动化都不会感到陌生。我们常常会听到一定软件测试人员一定要学自动化的说法&#xff0c;那么很多人都会有这样的疑问&#xff0c;从事软件测试为什么要学自动化&#xff1f;事实上&#xff0c;如今只会功能测试的从业者往往…

光波导成为AR眼镜迭代新趋势,二维扩瞳几何光波导潜力彰显

关注AR眼镜的朋友可能都会发现&#xff0c;近期新品迭代的一个趋势是持续在小型化、轻量化方向演进。与一年前光学方案主要以BirdBath不同的是&#xff0c;消费级AR眼镜正快速向光波导方案探索和转变。这一点在最近发布的众多新品AR眼镜中就能明显的感受到&#xff0c;以视享G5…

堆排序 TopK 优先级队列的部分源码 JAVA对象的比较

一.堆排序:我们该如何借助堆来对数组的内容来进行排序呢&#xff1f; 假设我们现在有一个数组&#xff0c;要求从小到大进行排序&#xff0c;我们是需要进行建立一个大堆还是建立一个小堆呢&#xff1f; 1)我的第一步的思路就是建立一个小堆&#xff0c;因为每一次堆顶上面的元…

MGRE和ospf的综合运用

目录实验需求知识点实验过程一&#xff0c;在R1 R2 R3中的MGRE搭建二&#xff0c;在R1 R4 R5中的MGRE搭建三&#xff0c;整个内网的ospf协议实验需求 一 题目要求 1&#xff0c;R6为ISP代表运营商&#xff0c;只能配置ip地址&#xff0c;R1-R5环回 代表私有网段 2&#xff0c…

【Linux】顶级编辑器Vim的基本使用及配置

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux 文章目录一、前言二、vim基本概念三、vim 基本操作1、模式切换2、命令模式3、插入模式4、底行模式四…

[Vue的数据绑定]一.Vue的数据绑定;二.Vue的事件绑定;三.Class和Style的绑定;四.Vue的过滤器;五.Vue脚手架的使用

目录 一.Vue的数据绑定 1.单向数据绑定&#xff1a;将Model绑定到View上&#xff0c;当通过JavaScript代码改变了Model时&#xff0c;View就会自动刷新。不需要进行额外的DOM操作就可以实现视图和模型的联动 &#xff08;1&#xff09;插值表达式&#xff1a;{{ 表达式 }}&am…

pandas 使用

import pandas as pd a [3,7,2] myvar pd.Series(a,index["x","y","z"]) print(myvar["x"]) #结果是3 #给序列赋值index &#xff0c;然后可以根据index找到对应的数 data {"col1": range(-5,0), "col2": r…

半入耳式耳机适合跑步吗、最适合跑步的五款耳机分享

好的跑步耳机可以帮助您跑得更努力、更快和更久。研究发现&#xff0c;听音乐可以提高跑步成绩&#xff0c;尤其是在速度方面。同时&#xff0c;随着音乐锻炼可以提高跑步者的速度并减少精神疲劳&#xff0c;帮助您自信地打破这些束缚。那么目前市面上有哪些无线耳机是适合跑步…

【算法】分治算法(第三章习题解答)

3 分治算法 3.1 设 AAA 是 nnn 个非 000 实数构成的数组, 设计一个算法重新排列数组中的数, 使得负数都排在正数前面. 要求算法使用 O(n)O(n)O(n) 的时间和 O(1)O(1)O(1) 的空间. 算法设计&#xff1a;由于算法要求使用 O(n)O(n)O(n) 的时间和 O(1)O(1)O(1) 的空间&#xff0…

力扣sql入门篇(八)

力扣sql入门篇(八) 1 订单最多的客户 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 SELECT customer_number FROM Orders GROUP BY customer_number having count(order_number)(SELECT max(o1.number)FROM (SELECT count(order_number) numberFRO…

电压放大器在超声导波声弹特性的液压管路压力检测中的应用

实验名称&#xff1a;电压放大器在超声导波声弹特性的液压管路压力检测中的应用 研究方向&#xff1a;超声检测 实验目的&#xff1a; 为实现农机装备液压系统的非介入式压力检测&#xff0c;在分析适合压力检测的超声导波声弹敏感模态与激励频率的基础上&#xff0c;搭建了一套…

用埃式筛法来求解素数。————C++

目录埃式筛法埃式筛法求解某一个数字包含的所有素数数组Code运行结果埃式筛法判断某一个数字是否为素数Code运行结果埃式筛法 首先要了解什么式埃式筛法之前&#xff0c;需要知道一个定理。 就是素数的整数倍一定不是素数。 了解了这个就基本大概懂了埃式筛法。 首先初始化…

C语言线性表实现:顺序表

文章目录&#xff1a;概念理解&#xff1a;1. 动态顺序表结构体&#xff1a;2. 顺序表动态初始化&#xff1a;3. 顺序表扩容&#xff1a;4. 插入&#xff1a;5. 删除&#xff1a;6. 按位序查找&#xff1a;7. 按值查找&#xff1a;8. 输出顺序表&#xff1a;9. 判断顺序表是否相…

UniSCA漏洞优先级排序

当谈到开源漏洞时&#xff0c;我们会发现它们似乎永远处于增长状态。事实上&#xff0c;UniSCA的开源风险报告显示&#xff0c;与2021年同期相比&#xff0c;2022年前9个月添加到UniSCA漏洞数据库中的开源软件漏洞数量增长了33% 。然而&#xff0c;虽然有些漏洞会带来严重的业务…

Altium Designer 20 凡亿教育视频学习-04

第四部分学习 unknow pin错误原因 将原理图导入到PCB时会出现问题 管脚未使用报错 导入PCB器件全部绿 删掉下面的紫红色框架即可&#xff08;Sheet2&#xff09; 视频讲的方法是&#xff1a;一开始把所有的规则删掉&#xff0c;只保留电气规则 板子大小评估-把器件聚集在一…

16---实现权限菜单管理(一)

1、实现角色管理 建role表 USE management; DROP TABLE IF EXISTS role;CREATE TABLE role (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,name varchar(50) DEFAULT NULL COMMENT 名称,description varchar(255) DEFAULT NULL COMMENT 描述,PRIMARY KEY (id) ) ENGINEInn…

活动星投票最美空姐网络评选微信的投票方式线上免费投票

如何进行“最美空姐”的投票活动_投票小程序投票_投票助力方式大家在选择投票小程序之前&#xff0c;可以先梳理一下自己的投票评选活动是哪种类型&#xff0c;目前有匿名投票、图文投票、视频投票、赛事征集投票等。我们现在要以“最美空姐”为主题进行一次投票活动&#xff0…