首页 > 其他分享 >七、二叉树之链式结构(递归)

七、二叉树之链式结构(递归)

时间:2024-10-17 22:52:42浏览次数:3  
标签:return 递归 BTNode 二叉树 链式 NULL root 节点

一、前序:

前面章节所说二叉树的结构其实是递归的,为什么这样说呢?根节点有左右子树,根节点的左右孩子又有左右子树,以此类推,直到叶子节点,它的左右子树为NULL,开始返回。所以,我们把它分成一个又一个的子树来分析。因此,它的结构是递归的。

因为一开始接触二叉树,还不是熟悉,我们先来手动创建一个二叉树,此方法不是二叉树的创建方法,只是为了快速进入二叉树的学习,真正的创建下面会讲。

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
BTNode* CreatBinaryTree()
{
	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 = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}

回顾一下二叉树的知识,二叉树: 

1.空树

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

二、二叉树的创建

所谓二叉树的遍历,就是按照某种特定的规则依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。

BTNode* BuyNode(BTDataType a)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = a;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = BuyNode(a[*pi]);
	(*pi)++;

	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);

	return root;
}

我相信这幅图已经非常清晰的解释了上段代码递归的理解,那么就可以理解树的结构是递归的 。我再解释一下:首先创建一个节点,根节点去寻找左孩子,左孩子又是左子树的根节点,继续去找,直到找到叶子节点,叶子节点的左右孩子存在,但是为空,开始返回,返回叶子节点的父亲节点,叶子节点的父亲节点又有右孩子,以此类推,直到返回到根节点(A)的右子树,那么就很好的诠释了树的结构是递归的。

三、二叉树的遍历 

3.1.二叉树的前序遍历

根节点、左子树、右子树。(根、左、右)遍历的结果是ABDGECF。

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

3.2.二叉树的中序遍历 

左子树、根节点、右子树。(左、根、右)遍历的结果是GDBEACF。

void InOrder(BTNode* root) {
	if (root == NULL){
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);
}

图和前序遍历基本上没有什么区别,只是打印的顺序不同了而已。 

3.3.二叉树的后序遍历

左子树、右子树、根节点。(左、右、根)遍历的结果是GDEBFCA。(也是一样的图理解)

void PostOrder(BTNode* root) {
	if (root == NULL){
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
    printf("%c ", root->data);
}

3.4.二叉树的层次遍历

层次遍历就是一层一层的遍历,那么前面我们讲过了队列,如果对队列还不是很熟悉,请大家往回去看队列的博客。

就是把根节点放到队列中,根节点出队列时,就让其的左右孩子进队列。

关于队列的相关代码,大家可以去队列的相关博客观看。 

// 层序遍历
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		printf("%c ", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}

	printf("\n");
	QueueDestory(&q);
}

四、二叉树的实现

4.1.二叉树的建立

BTNode* BuyNode(BTDataType a)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = a;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = BuyNode(a[*pi]);
	(*pi)++;

	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);

	return root;
}

4.2.二叉树的销毁

如果理解了上面所说的后序,使用后序来销毁是最方便的,因为后序最后才会返回根节点,前序和中序都是途中就会返回根节点,如果根节点已经被删掉了的话,就不方便进行下一步free,但可以定义一个变量来保存它的左右子树,则可以继续进行。

// 二叉树销毁
void BTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	BTreeDestory(root->left);
	BTreeDestory(root->right);
	free(root);
}

4.3.二叉树的节点个数

也是递归,如果递归到最后为空节点了或者根节点本身就是空节点,就是为0,否则就是+1。

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

4.4.二叉树的叶子节点个数

很好理解,就是寻找它左右孩子都是为NULL的,就是叶子节点。

int BTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
		return 1;

	return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}

4.5.二叉树查找值为X的节点

这个我们拿ABCDE五个节点建成的树简单展示一下。

// 二叉树查找值为x的结点
BTNode* BTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)
		return root;

	BTNode* ret1 = BTreeFind(root->left, x);
	if (ret1)
		return ret1;

	//return BTreeFind(root->right, x);
	BTNode* ret2 = BTreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}

4.6.第K层的节点个数,K>=1

只要是一个节点往下递归了,那么深度就是-1,直到k减为1,开始返回。

// 第k层的节点的个数,k >= 1
int BTreeKLevelSize(BTNode* root, int k)
{
	assert(k >= 1);

	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

	return BTreeKLevelSize(root->left, k - 1)
		+ BTreeKLevelSize(root->right, k - 1);
}

4.7.判断二叉树是否为完全二叉树

根据完全二叉树的形状,其实就是层次遍历,如果中有NULL节点,那么肯定就不是一个完全二叉树。

// 判断二叉树是否是完全二叉树
bool BTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
			break;

		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		// 空后面出到非空,那么说明不是完全二叉树
		if (front)
		{
			QueueDestory(&q);
			return false;
		}
	}

	QueueDestory(&q);
	return true;
}

至此,二叉树的顺序结构和链式结构就结束了,学习完两篇,实现二叉树其实并不简单,不仅要明白递归,还需要使用队列。但是没有关系,学习嘛,就是这样子环环相扣,才能更加理解。

如果不正之处,欢迎大家私信我进行纠正,制作不易,感谢大家的点赞!

标签:return,递归,BTNode,二叉树,链式,NULL,root,节点
From: https://blog.csdn.net/2201_75956982/article/details/143001049

相关文章

  • Java数据结构二叉树面试题精华(画图详解)
    前言:    针对二叉树,因为涉及到递归,需要跟多的练习强化递归的思想,其中也包括需要画图理解一些想不通的问题来提升自己!    一下面这些题为例,一起来提升自己的逻辑思维能力!(可能其中一些题已经写过,但是希望能再写一遍有助于提高代码能力)相同的树:      ......
  • 1601 添加运算符 枚举 递归dfs
    #include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e3+10,inf=0x3f3f3f3f;inta[N],vis[N];intn,ans;//计算函数:根据运算符i对sum和a[x]进行运算intcal(intsum,inti,intx){if(i==1)returnsum+a[x];//加法......
  • 手撸二叉树——AVL平衡二叉树
    还记得上一篇中我们遗留的问题吗?我们再简要回顾一下,现在有一颗空的二叉查找树,我们分别插入1,2,3,4,5,五个节点,那么得到的树是什么样子呢?这个不难想象,二叉树如下:树的高度是4,并且数据结构上和链表没有区别,查找性能也和链表一致。如果我们将树的结构改变一下呢?比如改成下面的树结构,那......
  • 树、森林与二叉树的转换
    一、引言与问题引出        在计算机科学的数据结构领域中,树、森林与二叉树之间的转换具有重要意义。在实际研究过程中,我们常常会发现树的结构过于复杂,而二叉树相对简单。例如,普通的树形结构使用程序语言描述起来相对复杂,而二叉树则相对容易。一颗普通的树可以通过孩......
  • 【题解】【记忆化递归】——Function
    【题解】【记忆化递归】——FunctionFunction题目描述输入格式输出格式输入输出样例输入#1输出#1提示数据规模与约定1.思路解析2.AC代码Function通往洛谷的传送门题目描述对于一个递归函数w......
  • 05 线性结构——队列(特性、入队与出队、顺序队列和链式队列、顺序队列的“假溢出”问
    目录1队列的基本概念1.1定义1.2队列的组成部分1.3空队列1.4操作流程 1.4.1添加元素(入队)1.4.2删除元素(出队)2队列的存储结构2.1顺序队列2.2链式队列3 顺序队列中的“假溢出”问题及解决方案3.1问题描述3.2解决方案方法1:元素左移法方法2:循环队列4......
  • 代码随想录|二叉树part 03
    求普通二叉树的节点数量要点:将子节点的个数记录到父节点,使用后序遍历classSolution{public:intmaxDepth(TreeNode*root){if(root==NULL)return0;intleft1=maxDepth(root->left);intright1=maxDepth(root->right);intnum=......
  • 代码随想录|二叉树part 02
    翻转二叉树要点:指针交换优先利用前序或后序遍历,若采用中序遍历(则两次采用遍历左子树)classSolution{public:TreeNode*invertTree(TreeNode*root){if(root==NULL)returnroot;swap(root->left,root->right);invertTree(root->left);......
  • 代码随想录算法训练营day17| 654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜
    学习资料:https://programmercarl.com/0654.最大二叉树.html#算法公开课用前序遍历构造二叉树二叉搜索树的特点,其左节点的值<每个节点的值<其右节点的值,且根节点的值大于它的左子树的所有节点的值,小于它右子树的所有节点的值,其他同理。二叉搜索树的搜索和验证时不关心遍历顺序,因......
  • Ouroboros3D-一种通过3D感知递归扩散生成3D模型的框架在win10系统上的复现流程
    本文将全程记录自己的Ouroboros3D(以下简称o3d)的环境配置以及训练过程,遇到的问题及解决办法。(Windows)目录一、o3d的安装及环境配置1.下载o3d项目2.anaconda、vscode安装及环境创建3.CUDA安装及环境变量的配置4.相应版本的pytorch的安装(1)在anaconda终端(2)在其他终端如vsco......