带你理解二叉树递归结构

理解二叉树的递归结构

看这段之前,建议大家先了解一下函数栈帧 递归刚开始学起来可能是比较懊恼,但是后期会用了以后,我们就会爱上递归,反而觉得非递归才更懊恼

我们先看一段代码

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

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

BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

BTNode* CreatTree()
{
	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;
}

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

	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

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

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

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

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

//int size = 0;
//void TreeSize(BTNode* root)
//{
//	if (root == NULL)
//		return;
//
//	++size;
//	TreeSize(root->left);
//	TreeSize(root->right);
//}

//void TreeSize(BTNode* root, int* psize)
//{
//	if (root == NULL)
//		return;
//
//	++(*psize);
//	TreeSize(root->left, psize);
//	TreeSize(root->right, psize);
//}

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

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

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

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

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

int main()
{
	BTNode* root = CreatTree();
	PreOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");

	/*size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);*/

	/*int size1 = 0;
	TreeSize(root, &size1);
	printf("TreeSize:%d\n", size1);

	int size2 = 0;
	TreeSize(root, &size2);
	printf("TreeSize:%d\n", size2);*/

	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeHeight:%d\n", TreeHeight(root));
	printf("TreeKLevel:%d\n", TreeKLevel(root, 3));
	printf("TreeKLevel:%d\n", TreeKLevel(root, 4));





	return 0;
}

首先我们看此段代码的二叉树图形如下图
在这里插入图片描述

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

前序遍历的递归解读

我们先看前序遍历
在这里插入图片描述
前序遍历的代码理解,无非就是根、左子树,右子树
用图来理解
在这里插入图片描述
为了方便理解,我们加上NULL,实际是不存在的。在树中,前序遍历顺序是根、左子树、右子树,如何来理解这种递归呢。
在这里插入图片描述
在这里插入图片描述

第一次遍历,根是1,printf,接着到1的左子树2,printf,再进到左子树3,printf,再进,为空,返回到3,接着再进到右子树,为空,返回到2,再进右子树,为空返回到1,再进1的右子树,为4,printf,进4的左子树到5,printf,再进5的左子树,为空,返回到5,在进5的右子树,为空返回到4,进4的右子树,为6,printf,再进6的左子树,为空返回到6,递归结束。

中序遍历的递归解读

在这里插入图片描述
看完了前序遍历,中序遍历还难吗?中序遍历就是,左子树,根,右子树
在这里插入图片描述

第一次遍历到1,进1的左子树2,再进2的左子树3,再进3的左子树为空返回到3,3为根printf,进3的右子树为空返回到2,2为根print,再进2的右子树为空返回到1,1为根printf,再进1的右子树到4,再进4的左子树到5,再进5的左子树为空返回到5,5为根printf,再进5的右子树为空返回到4,再进4的右子树到6,进6的左子树为空返回6,6为根printf,进6的右子树为空返回,递归结束。

后序遍历的递归解读

在这里插入图片描述
最后我们再看后序遍历,即左子树、右子树、根
在这里插入图片描述
第一次遍历到1,一路向左到3的左子树为空返回到3,进入3的右子树为空,返回到3,3为根printf,返回到2,进入2的右子树为空返回到2,2为根printf返回到1,进入1的右子树到4,一路向左到5的左子树为空返回5,进入5的右子树,为空返回5,5为根printf,返回4,进入4的右子树到6,进入6的左子树,为空返回6进入6的右子树,为空返回6,6为根printf,返回4,4为根printf,返回到1,1为根printf。

怎么样,现在看递归是不是很简单。

求二叉树size的递归解读

在这里插入图片描述
对于此递归,我们只要理解成一路向左遍历,在遍历右边,最后加上自身,即为树的size。
解读一下就是:第一次遍历是1,1!=NULL,再进到2,2!=NULL,到3,3!=NULL,到3的左子树为NULL,返回0,再到3的右子树,为空返回0,回到3,此时3的左右都为空,所以有+1,则返回到3的时候是1,再返回到2,进入2的右子树为空,返回0,再到2,2的左子树有1个,右子树有0,+1返回到1,此时1的左子树有2个,进入1的右子树到4,在进入4的左子树到5,同理,返回到4时,4的左子树有1个,进入到4的右子树6,同理返回到4时左右都有一个,一共有两个,再+1(为什么+1呢,1是自己)返回到1,1的左右相加,再+1(即加上自己)返回,即为树的size

计算高度的递归解读

在这里插入图片描述
对于此段代码,可能递归起来难以理解
那我们先解读下面这个二叉树(使用的是上图代码遍历,NULL在下图二叉树是不存在的,方便理解,所以我把加上了)
在这里插入图片描述
第一次返回是8的左子树,因为是一路向左,第二次返回是8的右子树,因为左子树遍历完要遍历右子树,两个都返回0,这时候8这个节点就知道自己的左右子树高度为0,所以自己的高度就是1,返回,节点4的左子树遍历完了,就去遍历右子树,右子树高度为0返回,所以节点4的高度就是左右子树的最高高度+1,返回2,节点2的左子树遍历完,遍历右子树,到节点5,节点5遍历自己的左子树为0,右子树为0,所以节点5的高度为1,节点2现在左子树遍历完了,右子树遍历完了,节点2的高度就是左右子树的最高高度+1,等于3,接着就遍历节点1了,节点1的左子树高度已经知道了,就接着遍历右子树,最终就是节点1左子树高度知道,右子树高度知道,找出最大值+1就是整个树的高度了

现在我们在看上面代码片段
在这里插入图片描述
大家按照我的方式自己理解一遍,我就不做解读了。

在这之后,我们在看一个求高度的代码
在这里插入图片描述
用这种方法求高度,可行吗,即使可行,那又有什么缺陷呢?请大家解读一下这段代码。
不妨用一个简单的树来试一遍
在这里插入图片描述
这种方法确实能够求出结果,但是其内部非常恐怖,所以这种写法在以后很多地方都是不建议使用的,换句话来说,就是比较挫。
这种递归,先下去递归比较,然后再重新递归拿结果,这种呈指数形式的增长,非常恐怖,打个比方就是,领导对你说句话,等你走了,再回来问领导刚说了什么。这样阶层多了以后,假设有十个人,那最后一个人每次比较完之后,都要被问一次。这样,遍历起来,这个效率就会变得非常低。所以不提倡此种写法。

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值