一篇文章解决二叉树递归问题

目录

一、二叉树与递归

二、前中后序遍历

三、例题

1、二叉树的销毁

2、二叉树节点个数

3、二叉树第k层节点个数

四、更多题解


一、二叉树与递归

关于二叉树的考题有很多,难度有高有低,但大多数都是考递归,学会了二叉树的递归就可以拿捏这棵树。

     

递归是二叉树最独特的特点,如图:

每个节点都可以当作一个根节点,并和他的孩子节点构成一棵树

而这个节点的孩子节点也可以当作一个根,和他的孩子节点构成一棵树

如此反复,孩子越来越少,直到最后只剩下一个叶子节点。

这就是递归分治的思想:大事归小,小事返回 

常见的题目如下: 

在二叉树中,不管何种遍历方式(层序除外),都是递归的思想

细分为以下三种遍历:前序遍历、中序遍历、后序遍历

    


二、前中后序遍历

遍历顺序:

前序:自己、左节点、右节点

中序:左节点、自己、右节点

后序:左节点、右节点、自己

 前序遍历: (绿色是到达该节点,橙色是遍历该节点)

         

中序遍历:

         

后序遍历:

写递归一定不能缺少的

1、返回条件:从上往下找左右子树,找到叶子节点就不再往下找了。也就是当此节点的左右子树为NULL时,就返回

2、参数的改变:由于每棵树的左右孩子节点都可以构成一棵新树,所以我们就递归左右孩子节点(root->left 、root->right)直到左右孩子都满足返回条件时递归停止。

所以我们每次递归的时候,做递归左右子树操作(绝大多数情况下采用先左后右,但先右后左也行)

可以形象的比作把任务交给孩子去做,然后孩子又交给他的孩子去做,直到最后的孩子没有孩子了,就自己做了(递归开始返回)

而下述的遍历我们采用了打印的方式,来清晰直观的展示遍历的顺序

切记!绝大多数情况下遍历并非采取打印的方式

//前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)//返回条件
	{
		return;
	}
	printf("%c", root->_data);//遍历自己
	BinaryTreePrevOrder(root->_left);//遍历左节点
	BinaryTreePrevOrder(root->_right);//遍历右节点
}
//中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)//返回条件
	{
		return;
	}
	BinaryTreeInOrder(root->_left);//遍历左节点
	printf("%c", root->_data);//遍历自己
	BinaryTreeInOrder(root->_right);//遍历右节点
}
//后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)//返回条件
	{
		return;
	}
	BinaryTreePostOrder(root->_left);//遍历左节点
	BinaryTreePostOrder(root->_right);//遍历右节点
	printf("%c", root->_data);//遍历自己
}

      


三、例题

1、二叉树的销毁

如何销毁?

很简单:遍历一遍,把每个节点都遍历到,每次遍历到的节点,我们都把他删掉,就实现了。

销毁采用后序遍历 

如果采用先序遍历,

当你free掉当前节点时,会找不到他的孩子节点

       

如果采用中序遍历,

你会成功free掉他的左孩子,但是当你想找右孩子时,会发现节点已经被free掉了,找不到右孩子

         

所以采用后序遍历删除

 代码如下:第一个是传二级指针,第二个是传一级指针

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)//二级指针目的是每次删除完一个节点时,都使指向该节点的指针转为指向NULL,防止产生野指针
{
	if (*root == NULL)//为空返回
		return;
	BinaryTreeDestory(&(*root)->_left);//找左节点
	BinaryTreeDestory(&(*root)->_right);//找右节点
	free(*root);
	*root=NULL;//每一步释放完root所指向空间的内存时,都把root指向的内容置空,防止产生野指针
	return;
}


//当然,也可以传一级指针,不过需要自己手动置空一下
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)//为空返回
		return;
	BinaryTreeDestory(root->_left);//找左节点
	BinaryTreeDestory(root->_right);//找右节点
	free(root);
	return;
}

       


2、二叉树节点个数

只需要遍历全部节点,每遍历到一个节点,总个数就+1

无论采用何种遍历方式,均能实现

思路有了,然后就是如何写递归了

1:我们需要的是节点个数,返回值肯定是 int 型的,所以函数的返回类型是 int 型

   

2:采用哪种递归方式,

        是单独递归,return返回?

        还是在return后面递归?

其实很好抉择,如果我们选择左图,则无论递归多深,返回值都是1

所以对于计数类型,我们通常都会选择右图,其每次递归都会使得数值+n

    

3:然后我们大事归小,小事返回。我们不想做的就交给我们的孩子做:只要我们自己不为空,就给节点个数+1,再加上左右子树的节点个数就行了,至于左右子树右多少节点,就递归给他们自己解决(这就是分治的思想)

int BinaryTreeSize(BTNode* root)//这里的代码是后序遍历,把+1更改位置就可以更改前中序遍历
{
	if (root == NULL)//如果为空就返回0
		return 0;
	return BinaryTreeSize(root->_left)+BinaryTreeSize(root->_right)+1;//只要我不为空,我就给个数+1,然后左右子树就递归给他们去算
}

          


3、二叉树第k层节点个数

只需要找到第k层,然后把第k层的节点个数相加,就实现了

思路有了,开始写递归

1:我们需要的是节点个数,返回值肯定是 int 型的,所以函数的返回类型是 int 型

2:我们要找第k层,所以传进来的参数除了root外,还要有一个k

3:每次递归时,逻辑上都是往深走了一层,当走到第k层时,就到达了我们想要的那一层。那么我们需要改变的参数就是k的值

4:当我们递归到我们想要的层数时,此时k恒等于一个常数。当k等于这个常数的时候,我们就返回+1,代表找到了一个符合规定的节点,并且停止往深递归

5:万一有些点的深处不到k,则我们到NULL时就要返回+0了,代表我们找不到第k层

6:然后我们大事归小,小事返回。我们不想做的就交给我们的孩子做:只要我们自己不到第k层,就把任务交给孩子去找,至于孩子有没有满足到达第k层,就递归给他们自己解决(这就是分治的思想)

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)//走到底了,还没到第k层,就返回0
		return 0;
	if (k == 1)//走到了第k层,就返回1
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1)+BinaryTreeLevelKSize(root->_right, k - 1);//每次递归都是往深一层递归,所以剩余层数-1(即k-1),然后就把问题交给孩子去做就行了
}

          


四、更多题解

如果还需要更多例题和题解:可以进入仓库查看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值