二叉树链式结构的实现(递归的暴力美学!!)

news2024/11/15 5:01:58

前言

Hello,小伙伴们。你们的作者菌又回来了,前些时间我们刚学习完二叉树的顺序结构,今天我们就趁热打铁,继续我们二叉树链式结构的学习。我们上期有提到,二叉树的的底层结构可以选为数组和链表,顺序结构我们选用的数组,那我们就不难知道,二叉树的链式结构采用链表为底层结构。

1.实现链式二叉树

用链表来表示一颗二叉树,即用链表来表示元素之间的逻辑关系。通常的方法就是链表中的每一个节点有三个域组成,数据域和左右指针,左右指针分别用来给出该节点左右孩子所在的链接点的存储地址,数据结构的定义如下:

typedef int BTDataType;
// ⼆叉链
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left; // 指向当前结点左孩⼦
struct BinaryTreeNode* right; // 指向当前结点右孩⼦
BTDataType val; // 当前结点值域
}BTNode;

不要忘记,我们在实现数据结构时,都要先创建三个文件来使测试代码变得更加的方便:

为了更好的演示效果,我们就在Test.c文件中手动的创建几个节点

BTNode* BuyBTNode(int val)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	} 
	newnode->val = val;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}  
BTNode* CreateTree()
{
	BTNode* n1 = BuyBTNode(1);
	BTNode* n2 = BuyBTNode(2);
	BTNode* n3 = BuyBTNode(3);
	BTNode* n4 = BuyBTNode(4);
	BTNode* n5 = BuyBTNode(5);
	BTNode* n6 = BuyBTNode(6);
	BTNode* n7 = BuyBTNode(7);
	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;
	n5->left = n7;
	return n1;

}


int main()
{
	BTNode* boot = CreateTree();
	return 0;
}

我们已经学习了链表,所以这里创建节点的操作我们就不再过多的赘述,因此,boot就是我们创建的这颗树的根节点。

接下来我们来回顾一下二叉树的概念,二叉树分为空树和非空树

 根节点的左子树和右子树分别有是有子树节点、子树节点的左子树和右子树组成,因此二叉树定义是递归式的,后续链式二叉树的操作中基本都是按照该概念实现的!!

1.1前中后序遍历

二叉树的操作离不开树的遍历,我们先来看看二叉树的遍历有哪些方式:

 1.1.1遍历的规则

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

前序遍历:访问根节点的操作,发生在遍历其左右子树之前。

访问顺序:根节点、左子树、右子树。

中序遍历:访问根节点的操作发生在遍历其左右节点子树之间(间)

访问顺序:左子树、根节点、右子树

后序遍历:访问根节点的操作发生在遍历其左右子树的遍历之后

访问顺序:左子树、右子树、根节点

1.1.2 代码的实现

1.1.2.1 前序遍历(PreOrder)
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
} p
rintf("%d ", root->val);
PreOrder(root->left);
PreOrder(root->right);
}

这里我们就涉及到了之前学习函数栈帧的知识,我们可以通过图来理解

函数递归栈帧图:

 

最终我们测试可得:

可知这符合我们前序遍历的规则,因此我们就成功实现了前序遍历。

实现了前序遍历,其他的两个遍历就简单了,几乎和前序遍历一样,中后序遍历也沿用了递归的思想 

后面大家能否根据上面前序遍历的代码实现中序遍历和后序遍历呢?

大家不妨一试。

1.1.2.2中序遍历(InOrder)
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
} 
InOrder(root->left);
printf("%d ", root->val);
InOrder(root->right);
}

1.1.2.3后序遍历(PostOredr)
v
oid PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
} I
nOrder(root->left);
InOrder(root->right);
printf("%d ", root->val);
}
 

有序他们之间的相似性,在代码上的差异也就只是不同行的代码顺序不一样,但是转化为后面的函数栈帧却又不小的差别。但在本质上,却还是大差不差!!

1.2节点个数以及高度等

1.2.1节点计数

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root);

我们来想一想,怎样才能达到我们的目的呢?

假设我们现在有这样的二叉树:
 

所以从上所述,我们就能够得出计数代码:

int BinaryNodeCount(BTNode* root)
{
	if (root == NULL)
		return 0;
	return 1 + BinaryNodeCount(root->left) + BinaryNodeCount(root->right);

}

 代码测试:

这里我们创建的是这样的二叉树。

可知这样我们就实现了二叉树节点的计数!!

 1.2.2二叉树叶子结点个数

// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode*root)

有了前面写求所有节点个数的经验,其实这个不就不难了:

叶子结点,就是没有子节点的节点。即root->left = root->right = NULL.

所以在这里我们就有两种返回情况,当遍历到NULL时,我们返回0, 当递归到叶子结点时,我们就返回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);
}

我们可以先提前看看这颗二叉树有多少个叶子结点:

上面我们可以看出一共有3个没有子节点的节点,所以叶子结点的个数就是3

 

1.2.3 二叉树第K层的节点个数

// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

我们要得到二叉树第K层节点的个数,其实也并不难,我们还是通过画图的方式来解析过程。

假设我们要求的是第三层的节点数:

 代码实现:

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

测试一下:

 1.2.4二叉树的深度/高度

//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);

在这里我们可以想出怎样的思路呢?

我们还是要先画图求证:

int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftDepth = BinaryTreeDepth(root->left);
	int rightDepth = BinaryTreeDepth(root->right);
	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}

 代码测试:

 从上面我们事先创建的那棵树一样,树的深度为4.所以我们实现了我们的目的。

1.2.5二叉树查找置位x的节点

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

这个操作也十分的简单,我们只需要遍历二叉树,找到与目标值相等的节点,我们就将其返回。

我们先看看实现代码:

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;
	BTNode* left =BinaryTreeFind(root->left, x);
	if (left)
		return left;
	BTNode* right = BinaryTreeFind(root->right, x);
	if (right)
		return right;
	return NULL;
}

在这里我们还是要进行二叉树的左右子树遍历,但是我们为了提高效率,只要在一边找到了符合目标的节点我们就直接返回该节点!!

小伙伴们可以根据上面的知识,自行画出递归栈帧图。

我们来测试以下代码:

可知其成功的找到了,值为6的节点!! 

1.2.6 二叉树的销毁

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

销毁的逻辑也十分的简单了,通过了递归的操作,我们遍历所有不为NULL的节点并销毁,这里我们就直接写出代码,大家可以自己进行递归栈帧的推理:

void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;
}

代码测试:

进行销毁操作:

 2.代码展示

2.1Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Tree.h"
BTNode* BuyBTNode(int val)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	} 
	newnode->val = val;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}  
BTNode* CreateTree()
{
	BTNode* n1 = BuyBTNode(1);
	BTNode* n2 = BuyBTNode(2);
	BTNode* n3 = BuyBTNode(3);
	BTNode* n4 = BuyBTNode(4);
	BTNode* n5 = BuyBTNode(5);
	BTNode* n6 = BuyBTNode(6);
	BTNode* n7 = BuyBTNode(7);
	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;
	n5->left = n7;
	return n1;

}


int main()
{
	BTNode* root = CreateTree();
	PreOrder(root);
	printf("\n");
	InOredr(root);
	printf("\n");
	PostOrder( root);
	printf("\n");
	printf("TreeNodeCount: %d\n", BinaryNodeCount(root));
	printf("leafSize: %d\n", BinaryTreeLeafSize(root));
	printf("TreeLevelKSize: %d\n", BinaryTreeLevelKSize(root, 3));
	printf("TreeLevelDepth: %d\n", BinaryTreeDepth(root));
	BTNode* find = BinaryTreeFind(root, 6);
	printf("%s ", find == NULL ? "没找到" : "找到了!!");
	printf("返回节点的值为:%d\n", find->val);
	BinaryTreeDestory(&root);
	PostOrder(root);
	return 0;
}

2.2Tree.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<time.h>
#include<stdbool.h>
typedef int BTDataType;
// ⼆叉链
typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left; // 指向当前结点左孩⼦
	struct BinaryTreeNode* right; // 指向当前结点右孩⼦
	BTDataType val; // 当前结点值域
}BTNode;
//前序遍历
void PreOrder(BTNode* root);
//中序遍历
void InOredr(BTNode* root);
//后序遍历
void PostOrder(BTNode* root);
//计数二叉树的节点
int BinaryNodeCount(BTNode* root);
//求二叉树叶子节点的个数
int BinaryTreeLeafSize(BTNode* root);
// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);
// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

2.3Tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Tree.h"
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%d ", root->val);
	PreOrder(root->left);
	PreOrder(root->right);
}
void InOredr(BTNode* root)
{
	if (root == NULL)
		return;
	InOredr(root->left);
	printf("%d ", root->val);
	InOredr(root->right);
}
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}
int BinaryNodeCount(BTNode* root)
{
	if (root == NULL)
		return 0;
	return 1 + BinaryNodeCount(root->left) + BinaryNodeCount(root->right);

}
//求二叉树叶子节点的个数
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);
}
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftDepth = BinaryTreeDepth(root->left);
	int rightDepth = BinaryTreeDepth(root->right);
	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;
	BTNode* left =BinaryTreeFind(root->left, x);
	if (left)
		return left;
	BTNode* right = BinaryTreeFind(root->right, x);
	if (right)
		return right;
	return NULL;
}
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;
}

好,今天的学习就到这里,我们下期再见,拜拜!!

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

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

相关文章

大数据Flink(一百零六):什么是阿里云实时计算Flink版

文章目录 什么是阿里云实时计算Flink版 一、产品概述 二、产品架构 三、产品优势 什么是阿里云实时计算Flink版 阿里云实时计算Flink版是一套基于Apache Flink构建的⼀站式实时大数据分析平台&#xff0c;提供端到端亚秒级实时数据分析能力&#xff0c;并通过标准SQL降低业…

openEuler 自定义ISO制作(logo,名称,ISO)

前言 oecustom (openEuler customize) 是一套关于 openEuler iso 格式光盘映像的定制工具集。 工具用途iso_custom用于定制 openEuler iso 镜像&#xff0c;可以定制 openEuler iso 镜像的系统名称和安装界面图标等iso_cut用于裁剪 openEuler iso 镜像&#xff0c;参考 oemak…

暴食之史莱姆(河南萌新2024)

思路&#xff1a;单调栈&#xff08;分别统计左边小于等于当前大小的数量&#xff09; #include <bits/stdc.h>using namespace std; typedef long long ll; typedef double db; typedef long double ldb; typedef pair<int, int> pii; typedef pair<ll, ll>…

【超强论文干货】教大家一个水论文最快的方法,一天能找20个创新点!本科生_研究生_博士生一定要收藏!

前言 都这个时候了&#xff0c;别告诉我你还没找到论文创新点。哈哈哈&#xff0c;如果你真的卡在这里了&#xff0c;那么这个文章绝对是你的救星&#xff0c;今天我就给大家分享找论文创新点这一块的方法&#xff0c;让你一天之内至少找到十几个。这个方法不仅适合那些真心想…

(亲测)taro npm run dev:weapp 微信小程序开发者工具预览报错

目录 报错 解决办法 报错 taro项目build之后&#xff0c;在微信小程序工具软件预览报错。报错信息 Error: module prebundle/node_modules_taro_weapp_prebundle_index-29de7cbd_less-node_modules_taro_weapp_prebundle_index-c9d831.wxss.js is not defined, require args…

ubuntu sudo命令不需要密码

sudo vim /etc/sudoers1、注释掉 %sudo ALL(ALL:ALL) AL 2、添加 用户名 ALL(ALL:ALL) NOPASSWD:ALL保存&#xff0c;退出即可

VGG神经网络原理

一、VGG网络架构 二、VGG参数详解 三、VGG总结

@Scheduled注解定时任务未按时执行问题记录

文章目录 问题原因解决方案 问题 在一个项目中多处使用了Scheduled注解&#xff0c;有些任务是1分钟执行一次&#xff0c;有些任务是每天凌晨0点执行一次。但是发现本该在凌晨0点执行的任务在0点并没有执行&#xff0c;而是在凌晨1点多才执行&#xff0c;而且每次执行的时间还…

设计模式:详细拆解策略模式

策略模式 既然是详解&#xff0c;就不以案例开头了&#xff0c;直奔主题&#xff0c;先来看看什么是策略模式。 模式定义 定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可相互替换。本模式 使得算法可独立于使用它的客户而变化。 结构 Strategy&a…

win10 没有自动亮度选项注册表关闭屏幕亮度自动变化

1、 WinR &#xff0c;输入命令&#xff1a;regedit 回车 2、依次展开位置&#xff1a;HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class{4d36e968-e325-11ce-bfc1-08002be10318} 然后将0000和0001可能还有0002中的 KMD_EnableBrightnessInterface2 的值改为0即可。 …

亚马逊、ebay自养号测评方法:从零到一打造高效流量和销量

在跨境电商领域&#xff0c;如马逊、拼多多Temu、shopee、Lazada、wish、速卖通、煤炉、敦煌、雅虎、eBay、TikTok、Newegg、乐天、美客多、阿里国际、沃尔玛、OZON等平台上&#xff0c;卖家为了提升店铺的权重、流量及销量&#xff0c;常常需要进行产品测评。自养号测评作为一…

TransactionAspectSupport.currentTransactionStatus.setRollbackOnly 是什么作用,为什么这么写

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() 是Spring框架提供的一个方法&#xff0c;用于在事务执行过程中显式地指示当前事务需要回滚。 使用这个方法一般结合try catch使用&#xff0c;出现异常时&#xff0c;可以做到不抛出异常&#xff0c;但…

华为云耀服务器安装和使用MobSF进行APP基线检查

华为云耀服务器安装和使用MobSF进行APP基线检查 MobSF简介 Mobile Security Framework&#xff08;MobS&#xff0c;移动安全框架&#xff09;是一种自动化多平台移动应用程序&#xff0c;支持Android、iOS和Windows应用自动化测试。能够进行静态、动态分析&#xff0c;web A…

【unity小技巧】unity最完美的CharacterController 3d角色控制器,实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果,复制粘贴即用

最终效果 文章目录 最终效果前言为什么使用CharacterControllerSimpleMove和Move如何选择&#xff1f;1. SimpleMove2. Move 配置CharacterController参数控制相机移动跳跃方式一方式二 下蹲处理下坡抖动问题实现奔跑和不同移速控制完整代码补充&#xff0c;简单版本 实现物理碰…

--归并排序--

归并排序是我们研究的最后一种排序了&#xff0c;那什么是归并排序呢&#xff1f; 我们之前在力扣上面刷过一个题目&#xff0c;就是合并两个有序数组&#xff0c;其实也是和这个是一个思想借用第三 个数组&#xff0c;然后将第三个数组拷贝给a数组&#xff0c;这样就实现了排…

计算机毕业设计选题推荐-智慧物业服务系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

2024年08月05日截稿 | 第三届人工智能、物联网和云计算技术国际会议(AIoTC 2024)

第三届人工智能、物联网和云计算技术国际会议&#xff08;AIoTC 2024&#xff09; 2024 3rd International Conference on Artificial Intelligence, Internet of Things and Cloud Computing Technology 2024年9月13-15日 | 中国武汉 重要信息 大会官网&#xff1a;www.ic…

S200自检程序报错

现象&#xff1a;在ATE电脑断网的情况下&#xff0c;启动自检程序&#xff0c;提示如下报错&#xff1a; 解决方法&#xff1a;

大模型在RPA领域的应用与探索-代码生成

01. 前言 随着人工智能技术的飞速发展&#xff0c;大模型在多个领域的应用日益广泛。特别是在机器人流程自动化&#xff08;RPA&#xff09;领域&#xff0c;这些技术的进步为自动化任务的执行带来了显著的效率提升。然而&#xff0c;传统RPA在任务流程编排上依赖人工编写脚本…

如何在 VSCode 中使用驭码CodeRider?【实践篇】

极狐GitLab 在 5 月 28 日正式发布了 AI 产品驭码CodeRider&#xff0c;可以使用驭码CodeRider 进行AI 编程 & DevOps 流程处理。现已开启免费试用&#xff0c;登录官网&#xff1a;https://coderider.gitlab.cn/ 即可申请试用。 GitLab 中文版学习资料 驭码CodeRider 官…