二叉树的遍历和一些相关操作

大家好!这篇就讲一讲二叉树的遍历和一些相关操作,为什么不说二叉树的增删查改,因为二叉树的增删查改没有价值。我们要学会一些二叉树的操作,为以后学习打好基础。
在这里插入图片描述

1. 二叉树的遍历

1.1 二叉树遍历的概念

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

1.2 前序/中序/后序的递归结构遍历

前序遍历(先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
访问顺序就是:根->左子树->右子树

中序遍历——访问根结点的操作发生在遍历其左右子树之中(间)。
访问顺序就是:左子树->根->右子树

后序遍历——访问根结点的操作发生在遍历其左右子树之后。
访问顺序就是:左子树->右子树->根

补充:还有一种遍历叫做层序遍历:从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

下面举个实例看一下:
在这里插入图片描述
在这里插入图片描述
我来解释一下:
前序遍历:首先访问根,也就是1,然后访问左子树:
在这里插入图片描述
访问左子树,首先访问的也是根,也就是2,然后再访问左子树:
在这里插入图片描述
首先访问的还是根,也就是3,然后访问左子树,是NULL,是NULL就返回,
左子树访问结束,访问右子树,右子树是NULL,返回。
所以到这里,图2中红色框的树就访问完了,也就是2的左子树访问完了,就开始访问2的右子树,是NULL,就返回。
这样,图1中红色框的树就访问完了,也就是1的左子树访问完了,然后我们开始访问1的右子树:
在这里插入图片描述
首先,访问的是根,也就是4,然后访问4的左子树:
在这里插入图片描述
然后首先访问根,也就是5,然后访问5的左子树,是NULL,是NULL就返回,左子树访问完了,访问5的右子树,是NULL就返回。
这样图4里蓝色框的树就访问完了,也就是4的左子树访问完了,然后访问4的右子树:
在这里插入图片描述
一样的,先访问6,然后6的左子树是NULL,是NULL就返回,左子树访问完了,访问右子树,是NULL就返回。
这样4的右子树也访问完了,到这里1的右子树也访问完了。所以前序遍历就结束了。

其它的大家可以根据这个思路试一试。下面我还用这个图来说一下它的代码实现。

1.2.1 用代码实现前序遍历

此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
在这里插入图片描述
先序遍历的代码如下:

//前序遍历
void PrevOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

下面画图来看一下:
在这里插入图片描述

1.2.2 用代码实现中序遍历

中序遍历的代码如下:

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

下面画图来看一下:
在这里插入图片描述

1.2.3 用代码实现后序遍历

后序遍历的代码如下:

// 后序遍历
void NextOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	NextOrder(root->left);
	NextOrder(root->right);
	printf("%d ", root->data);
}

图解:
在这里插入图片描述

2. 二叉树的相关操作

我们还用上面的二叉树来举例:
在这里插入图片描述

2.1 求二叉树结点的个数

2.1.1 思路1:用局部变量count计数

在这里插入图片描述
这种方法是不行的,因为count是局部变量,每次递归count都会被置为0,所以结果只会是1。

2.1.2 思路2:静态的局部变量

在这里插入图片描述
这里就第一次可以,多次调用会有问题,没办法每次初始化为0。

2.1.3 思路3:用全局变量count计数

在这里插入图片描述
这样,在每次使用时,都给count置为0,就可以使用了。但这个会有线程安全的问题,这个以后linux就知道了。

2.1.4 思路4:遍历+计数

在这里插入图片描述
这样把count作为参数传进去,在遍历的时候增加,但每次也要自己置为0。

2.1.5 思路5:分治思想

什么是分治呢?分治就是:把复杂的问题,分成更小规模的子问题,子问题分成更小规模的子问题,直到子问题不能被分割,直接能出结果。

那么这道题,我们可以这样去思考:
1.空树,最小规模子问题,结点个数返回0。
2.非空,左子树结点个数+右子树结点个数+1(自己)。

在这里插入图片描述
首先,是1的结点,不是空树,所以我们要算1结点的左子树和右子树结点的个数,然后加上自己。
在这里插入图片描述
第二,我们算1的左子树结点,不是空树,我们要算2结点的左子树和右子树结点的个数,然后加上自己。
在这里插入图片描述
第三,2的左子树不是空树,我们要算3的左子树和右子树。然后加上自己,3的左子树和右子树都是空树,两边都返回0,加上自己就是1,所以2的左子树结点的个数为1。然后,我们要算2的右子树的结点的个数,是空树,返回0。

到这里,1的左子树就计算完了,为2个节点,然后我们要计算1的右子树。
1的右子树不是空树,所以我们要计算4的左子树和右子树,后面和前面的思路一样。
代码如下:

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

2.2 求二叉树叶子节点的个数

2.2.1 思路1:遍历+计数

这个和上面的思路是一样的,直接看代码:

void BTreeLeafSize(BTNode* root, int* pCount)
{
	if (root == NULL)
		return;

	if (root->left == NULL && root->right == NULL)
		++(*pCount);

	BTreeLeafSize(root->left, pCount);
	BTreeLeafSize(root->right, pCount);
}

2.2.2 思路2:分治

也是和上面的思路一样,分别统计左子树和右子树叶子节点的个数。当左子树和右子树都为空就返回。

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);
}

2.3 求第k层的节点的个数,k >= 1

解题思路:
1.空树,返回0
2.非空,且k==1,返回1
3.非空,且k>1,转换成左子树第k-1层节点的个数+右子树k-1层节点的个数

在这里插入图片描述
假设,我们要求这棵二叉树的第3层节点的个数。就可以转换成左子树第二层节点的个数+右子树第二层节点的个数(A的第三层就相当于它子树的第二层)
在这里插入图片描述
B,C的第二层又可以转换成它们子树的第一层。当k==1,我们就可以返回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);
}

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

2.4 求二叉树的深度

解题思路:左子树高度和右子树高度比较,大的那个+1
代码如下:

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

	int leftDepth = BTreeDepth(root->left);
	int rightDepth = BTreeDepth(root->right);

	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}

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

2.5 二叉树查找值为x的结点

解题思路:
假设,我们要在下面的二叉树中找H
在这里插入图片描述
首先,我们要在根找,根不是,我们要在它左子树找。
在这里插入图片描述
如果在左子树找到了,就不用在右子树找。如果没在左子树找,就去右子树找。那我们该如何找。
先找B,不是,然后去B的左子树找,D不是,然后去D的左子树找,D的左子树为空,返回一个NULL,然后去D的右子树找,D的右子树为空,返回一个NULL。所以,到这里B的左子树就没找到,D就会返回NULL给B,然后去B的右子树去找。
在这里插入图片描述
后面的思路一样。如果找到了就返回。
代码如下:

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;

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

	return NULL;
}

到这里,一些二叉树基本的操作就差不多,后面我们说一些OJ题来看一下。如果大家觉得有帮助,希望能够多多支持!谢谢大家!
在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学代码的咸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值