【数据结构初阶】第八篇——二叉树的链式结构(二叉树的前、中和后序遍历+层序遍历+链式结构的实现+相关简单的递归问题)

⭐️本篇博客我要来和大家一起聊一聊数据结构中的二叉树的链式结构的实现及相关的一些问题的介绍
⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/data-structure/commit/de7024a7498be71a78c18d22b7a7caee53f3ffb4


🌏二叉树的链式结构

前一篇博客介绍了二叉树的顺序结构,是通数组来存储的,这里我们通过创建链式结构来存储,在堆上申请空间,结构如下:

typedef char BTDataType;

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

在这里插入图片描述

🌏二叉树的简单创建

这里我们用最粗暴的方法创建一棵树,就是一个节点一个节点的创建,实现代码如下:

BTNode* CreatBinaryTree()
{
    BTNode* treeNode1 = (BTNode*)malloc(sizeof(BTNode));
	BTNode* treeNode2 = (BTNode*)malloc(sizeof(BTNode));
	BTNode* treeNode3 = (BTNode*)malloc(sizeof(BTNode));
	BTNode* treeNode4 = (BTNode*)malloc(sizeof(BTNode));
	BTNode* treeNode5 = (BTNode*)malloc(sizeof(BTNode));
	BTNode* treeNode6 = (BTNode*)malloc(sizeof(BTNode));

	treeNode1->data = 'A';
	treeNode2->data = 'B';
	treeNode3->data = 'C';
	treeNode4->data = 'D';
	treeNode5->data = 'E';
	treeNode6->data = 'F';

	treeNode1->left = treeNode2;
	treeNode1->right = treeNode3;
	treeNode2->left = treeNode4;
	treeNode2->right = treeNode5;
	treeNode3->left = treeNode6;
	treeNode3->right = NULL;
	treeNode4->left = treeNode4->right = NULL;
	treeNode5->left = treeNode5->right = NULL;
	treeNode6->left = treeNode6->right = NULL;
}

如下图:
在这里插入图片描述

🌏二叉树的遍历

💎前序遍历(递归实现)

🐬这里我先给大家介绍一下用递归实现,后面我会给大家介绍如何用栈模拟实现非递归。
🐬前序遍历指的是先遍历根,再遍历左子树,再遍历右子树。
🐬思想: 二叉树本身就是一种递归结构,所以通过递归来遍历这棵树,如何递归遍历呢?
是这样的,先遍历根,再遍历左子树,左子树又可以分解为,根、左子树和右子树,直到把所以左子树的部分遍历完,然后就遍历右子树,右子树又可以分解为,根、左子树和右子树
假如我们构建了一个PrevOrder,这个函数是可以实现前序遍历的,所以我们先遍历根,剩下的左子树和右子树遍历就交给这个函数去实现。
递归就是把一个大的问题一直分解,直到分解为最后一个小问题来解决,这就是所谓的大事化小

了解了上面那些,我们就来看一下代码是如何实现的,

void BinaryTreePrevOrder(BTNode* root)
{
    // 遍历到NULL就返回
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
  	// 先遍历根
	printf("%c ", root->data);
	// 左子树交给BinaryTreePrevOrder这个函数去遍历
	BinaryTreePrevOrder(root->left);
	// 右子树交给BinaryTreePrevOrder这个函数去遍历
	BinaryTreePrevOrder(root->right);
}

下面我们来看一下代码运行结果:
在这里插入图片描述

💎中序遍历(递归实现)

🐳中序遍历指的是先遍历左子树,再遍历根,再遍历右子树。
🐳思想: 二叉树本身就是一种递归结构,所以通过递归来遍历这棵树,如何递归遍历呢?
是这样的,先遍历左子树,左子树又可以分解为,左子树、根和右子树,直到把所以左子树的部分遍历完,然后就遍历根,再遍历右子树,右子树又可以分解为,左子树、根和右子树
如下图:
在这里插入图片描述
代码实现如下:

void BinaryTreeInOrder(BTNode* root)
{
	// 遍历到NULL就返回
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	// 左子树交给BinaryTreePrevOrder这个函数去遍历
	BinaryTreeInOrder(root->left);
	// 遍历根
	printf("%c ", root->data);
	// 右子树交给BinaryTreePrevOrder这个函数去遍历
	BinaryTreeInOrder(root->right);

}

代码运行结果如下:
在这里插入图片描述

💎后序遍历(递归实现)

🐇后序遍历指的是先遍历左子树,再遍历右子树,最后遍历根。
🐇思想: 二叉树本身就是一种递归结构,所以通过递归来遍历这棵树,如何递归遍历呢?
是这样的,先遍历左子树,左子树又可以分解为,左子树、右子树和根,直到把所以左子树的部分遍历完,然后遍历右子树,右子树又可以分解为,左子树、右子树和根,最后遍历根。
如下图:
在这里插入图片描述
代码实现如下:

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	// 左子树交给BinaryTreePrevOrder这个函数去遍历
	BinaryTreePostOrder(root->left);
	// 右子树交给BinaryTreePrevOrder这个函数去遍历
	BinaryTreePostOrder(root->right);
	//遍历根
	printf("%c ", root->data);

}

代码运行结果如下:
在这里插入图片描述

💎层序遍历

🍉 层序遍历: 设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
在这里插入图片描述
层序遍历用到的是队列来解决,先将根入队,然后把根取出,取出的同时分别再把不为空左节点右节点入队,直到队列为空时就说明二叉树已经遍历完了。为了方便大家理解我在这里做了个动图演示一下:
请添加图片描述

再看一下代码实现如下:

void BinaryTreeLevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL");
		return;
	}
	queue<BTNode*>q;
	q.push(root);

	while (!q.empty())
	{
		BTNode* front = q.front();
		q.pop();

		printf("%c ", front->data);
		if (front->left)
			q.push(front->left);
		if (front->right)
			q.push(front->right);
	}

}

代码运行结果如下:
在这里插入图片描述

🌎二叉树的节点个数和高度

🍍二叉树的节点个数

🌱此问题可以分解为求左子树节点个数+右子树节点个数+1,然后左子树节点个数又可以继续分,所以这里可以用递归来求解这个问题,下面我们就来实现一下这个接口

代码如下:

int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : 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层节点个数

☔️求解第k层节点个数其实就是求解左子树的第k-1层节点个数+右子树的第k-1层节点个数,当k==1时,就可以直接返回1,节点为空就返回0。

代码实现如下:

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

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

🔥这里我们只需要前序遍历一遍二叉树即可,直到找到x就返回。

代码实现如下:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* leftRet = BinaryTreeFind(root->left, x);
	if (leftRet)
		return leftRet;
	BTNode* rightRet = BinaryTreeFind(root->right, x);
	if (rightRet)
		return rightRet;
}

🌍二叉树的创建与销毁

🍓二叉树的创建

🍄问题: 通过前序遍历的数组"ABD##E#H##CF##G##"
给定一串字符串,#代表的是空树,其他的都是节点。
这里我们只需要前序遍历这串字符串来构建这棵树即可。
同样是用递归来解决这个问题
treeNode->left = BinaryTreeCreate(s, pi); 让当前这个节点的左指向给递归构建左子树
**treeNode->right = BinaryTreeCreate(s, pi);**让当前这个节点的右指向给递归构建左子树
如果当前节点为空就直接返回NULL

代码实现如下:

BTNode* BinaryTreeCreate(BTDataType* s, int* pi)
{
	if (s[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* treeNode = (BTNode*)malloc(sizeof(BTNode));
	treeNode->data = s[(*pi)++];
	treeNode->left = BinaryTreeCreate(s, pi);
	treeNode->right = BinaryTreeCreate(s, pi);
	return treeNode; 
}	

🍓二叉树的销毁

🌿二叉树的销毁我们可以通过层序遍历来把节点逐个释放掉,由于与上面的层序遍历很相似,这里就不过多介绍了,直接上代码

void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
		return;
	queue<BTNode*>q;
	q.push(*root);

	while (!q.empty())
	{
		BTNode* front = q.front();
		q.pop();
		
		if (front->left)
			q.push(front->left);
		if (front->right)
			q.push(front->right);
		free(front);
		front = NULL;
	}
}

🌐总结

这篇博客就主要介绍了二叉树的链式结构及实现,还有一些相关的问题,二叉树有关的问题递归会用到比较多,因为二叉树本身就是由递归来创建的。今天就介绍到这,喜欢的话,欢迎点赞、支持和指正~
在这里插入图片描述

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆兽学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值