【数据结构】二叉树经典递归题目(力扣965,100,101,144,572,牛客KY11)


本篇博客由CSDN@先搞面包再谈爱原创,转载请标注清楚,请勿抄袭。

oj题前的基础题目

二叉树主打的就是一个分治的思想,用递归可以解决很多的问题。

二叉树节点个数

大致思路:总结点个数 = 当前根节点(1)+左子树的结点个数+右子树的结点个数

递归,当根节点为空时停止

int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

二叉树叶子节点个数

大致思路:根节点的叶子节点的个数 = 左子树的叶子结点的个数 + 右子树的叶子结点的个数。

递归,当根节点为空时返回0(当某一结点的左右中有一个为空,那么不会返回数值,会访问左右子树,此时就会访问到空树;或者是整个树本身就是空的),当根节点的左右子树为空时返回1(叶子节点)。

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	else if (root->_left == NULL && root->_right == NULL)
	{
		return 1;
	}

	int LeftLeafSize = BinaryTreeLeafSize(root->_left);
	int RightLeafSize = BinaryTreeLeafSize(root->_right);

	return LeftLeafSize + RightLeafSize;
}

二叉树第k层节点个数

大致思路:根节点的第K层结点个数 = 左右子树的第k-1层结点个数。

递归,当根为空时就返回0,当k==1时就返回1。

在这里插入图片描述

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k >= 1);
	if (root == NULL)
	{
		return 0;
	}
	else if (k == 1)
	{
		return 1;
	}

	return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

二叉树查找值为x的节点

大致思路:先看根是不是,不是了看左子树里有没有x,再看右子树里有没有x,如果都没找到就返回空。

递归,当根节点为空时就返回NULL,否则对比根节点与x的值,如果相等,则返回根节点,如果不相等,则去左子树和右子树中找。

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

	if (root->_data == x)
	{
		return root;
	}

	BTNode* LeftNode = BinaryTreeFind(root->_left, x);
	if (LeftNode)
		return LeftNode;

	BTNode* RightNode = BinaryTreeFind(root->_right, x);
	return RightNode;
}

二叉树的高度(深度)

大致思路:二叉树的高度 = max(左子树高度,右子树高度) + 1;

递归,当根节点为NULL时返回0,否则就求左右子树的高度。

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

	int leftDepth = BinaryTreeDepth(root->_left);
	int rightDepth = BinaryTreeDepth(root->_right);

	int max = leftDepth > rightDepth ? leftDepth : rightDepth;

	return max + 1;
}

力扣

力扣965

在这里插入图片描述
大致思路:先看根是否等于左,再看根是否等于右,相等则继续,不等则停止。

递归,当根为空的时候返回true,不为空时先判断根和左(左不为空的前提下),如果不相等则返回false,如果相等则继续判断根和右(右不为空的前提下),如果不相等则返回false,如果相等,则返回左右子树的判断结果。

bool isUnivalTree(struct TreeNode* root)
{
    if(root == NULL)
        return true;
    
    //如果左不空,就判断左和根是否相等。
    if(root->left && root->val != root->left->val) 
        return false;//不相等就返回false
	//相等就继续判断根和右
	
	//如果右不为空,就判断根和右是否相等
    if(root->right && root->val != root->right->val)
        return false;//不相等就返回false    
	//相等就继续判断左右子树是否为单值二叉树
    return isUnivalTree(root->left) && isUnivalTree(root->right);    
}

力扣100

在这里插入图片描述

大致思路:先判断两树的根是否对称,再判断这两个数的左右子树是否对称。

递归,先看两树的根是否同时为空,若都为空,说明这两个节点是对称的,然后判断二者是否一个为空一个不为空,这样的话就不是对称的,然后就是两个不是空的情况,这时候先判断值是否相同,若不相同,说明不对成,若相同,则说明当前结点是对称的,但是还不能返回true,还要判断当前结点的左右是否对称。

bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    //左右都为空true
    if(p == NULL && q == NULL)
        return true;
    //左为空且右不为空 || 右为空且左不为空 false
    if((p == NULL && q != NULL) || (p != NULL && q == NULL))
        return false;
    //左右都不为空 先判断二者值是否相等
    if(p->val != q->val)
        return false;//不相等返回false

    //相等则继续判断二者的左右子树
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

力扣101

在这里插入图片描述
大致思路:与前一个题目非常相似,先判断根是否为空,不为空的话就和上面那个题一样了,判断左右是否同时为空,同时为空返回true,一个为空返回false,两个都不为空先判断值是否相同,然后再判断左左是否等于右右,左右是否等于右左是否是对称的。

这里的递归要自己创建一个子函数才能进行判断,不然本题目的函数的参数没法同时将两个结点进行判断。

bool _isSymmetric(struct TreeNode* left, struct TreeNode* right)
{
    //左右都为空 true
    if(left == NULL && right == NULL)
        return true;
    //左右两个结点一个为空一个不为空 false
    if((left == NULL && right != NULL) 
    || (left != NULL && right == NULL))
        return false;
    //都不为空,左右值不同 false
    if(left->val != right->val)
        return false;  

    return _isSymmetric(left->left, right->right)
        && _isSymmetric(left->right, right->left);
}

bool isSymmetric(struct TreeNode* root)
{
    //根为空 true
    if(root == NULL)
        return true;
    
    return _isSymmetric(root->left, root->right);
}

力扣144

在这里插入图片描述
大致思路:根打印的一模一样,只不过是把printf改成了数组赋值。但是这个题不能直接在一个函数里把递归写出来。因为题目所给的函数要返回一个数组,你若过这样递归的话返回值是没法搞定的,所以要搞一个子函数来进行递归。

下面这句话是题目当中的,意思就是当前题目所给的函数的返回数组必须得是动态开辟出来的,而且调用该函数的函数会自动释放掉你所开辟的空间。
在这里插入图片描述
那么前序遍历树,并将前序遍历数据的顺序放到一个数组中,就得要先决定一下数组的大小,数组的大小是由树来直接决定的,那么我们可以先写一个统计树的结点个数的函数,然后开辟空间的时候开辟节点个数个int类型就行了。

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

void _preorderTraversal(struct TreeNode* root, int* res, int* pi)
{
    if(root == NULL)
        return;

    res[(*pi)++] = root->val;
    _preorderTraversal(root->left, res, pi);
    _preorderTraversal(root->right, res, pi);
}

int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    *returnSize = TreeSize(root);
    int* res = (int*)malloc(*returnSize * sizeof(int));
    
    int i = 0;
    _preorderTraversal(root, res, &i);

    return res;
}

力扣572

在这里插入图片描述
大致思路:遇到root->val和subRoot->val相等的时候就判断一下当前root的位置是否为和subRoot相等,若相等就说明有该子树,不相等就继续判断root的左和右。

本题递归也要创建一个函数,用来判断两树是否相等的子函数。

//判断两树是否相等
bool isSameTree(struct TreeNode* root, struct TreeNode* subRoot)
{
    if(root == NULL && subRoot == NULL)
        return true;

    if((root == NULL && subRoot != NULL) || (root != NULL && subRoot == NULL))
        return false;

    if(root->val != subRoot->val)
        return false;

    return isSameTree(root->left, subRoot->left)
        && isSameTree(root->right, subRoot->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
    if(root == NULL)
        return false;

    //遇到根节点和sub的根节点的值相同了就对比,如果是子树就说明成立,返回true,无需再对比
    if(root->val == subRoot->val && isSameTree(root, subRoot))
        return true;

    //遇到根结点不等或者等了但不是子树,就要判断root的左右是否有该子树
    return isSubtree(root->left, subRoot)
        || isSubtree(root->right, subRoot);
}

牛客

牛客KY11

在这里插入图片描述

大致思路:数组中的每一个元素就是一个结点,遇到 ‘#’ 就是NULL,其他的就是自身的值创建的结点。

本题目需要理解透彻的话看一下这张图解就行了:
在这里插入图片描述
画的有点丑,凑合着看。
其实本质上还是递归构建树。
核心代码就是下面这点:

BTNode* TreeCreat(char* arr, int* pi)
{
	//遇到#了或者是字符串走完了就返回NULL
    if(arr[*pi] == '#' || arr[*pi] == 0)
    {
        (*pi)++;
        return NULL;
    }

    BTNode* node = BuyNode(arr[(*pi)++]);
    //递归构建左子树
    node->left = TreeCreat(arr, pi);
    //递归构建右子树
    node->right = TreeCreat(arr, pi);
	
	//返回当前结点
    return node;
}

完整代码:

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

typedef char BTDataType;
typedef struct TreeeNode 
{
    BTDataType data;
    struct TreeeNode* left;
    struct TreeeNode* right;
} BTNode;

//创建新结点
BTNode* BuyNode(BTDataType x)
{
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    node->data = x;
    node->left = NULL;
    node->right = NULL;

    return node;
}

//创建树
BTNode* TreeCreat(char* arr, int* pi)
{
    if(arr[*pi] == '#' || arr[*pi] == 0)
    {
        (*pi)++;
        return NULL;
    }

    BTNode* node = BuyNode(arr[(*pi)++]);
    node->left = TreeCreat(arr, pi);
    node->right = TreeCreat(arr, pi);

    return node;
}

//中序遍历
void InOrder(BTNode* root)
{
    if(root == NULL)
        return;

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

//销毁树
void DestroyTree(BTNode* root)
{
    if(root == NULL)
        return;

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

int main() 
{
    char arr[101] = {0};
    scanf("%s", arr);
    int i = 0;
    BTNode* root = TreeCreat(arr, &i);

    InOrder(root);

    DestroyTree(root);
    return 0;
}

二叉树的销毁

直接用后续遍历就行。

//销毁树
void DestroyTree(BTNode* root)
{
    if(root == NULL)
        return;

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

判断二叉树是否是完全二叉树

大致思路:用队列,和层序遍历思路一样,只不过入队的时候不管空不空都入队,每次循环之前都要先得到队头,再判断一下队头是否为空,对头不为空就入左右,如果为空那么这个位置就是队列里的第一个NULL,然后从这个位置往后都不能出现空,如果出现空了就说明不是完全二叉树,如果都为空的话就说明是完全二叉树。

看下图解:

完全二叉树

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

非完全二叉树

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

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	if (root == NULL)
		return 1;
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	//判断队列中找到NULL后面的元素是否有非空
	int flag = 0;
	while (!QueueEmpty(&q))
	{
		BTNode* tmp = QueueFront(&q);
		QueuePop(&q);
		if (tmp != NULL)
		{
			//tmp不为空就先入左右
			QueuePush(&q, tmp->_left);
			QueuePush(&q, tmp->_right);
		}
		else
		{
			//tmp为NULL,就判断其后方是否有非空
			while (!QueueEmpty(&q))
			{
				BTNode* node = QueueFront(&q);
				QueuePop(&q);
				if (node != NULL)
				{
					//遇到非空的结点了
					flag = 1;
					break;
				}
			}
		}
		if (flag == 1)
			break;//遇到非空结点了,不用再判断了
	}

	if (flag == 1)
		return false;
	else
		return true;

	QueueDestroy(&q);
}

int main()
{	
	BTNode* root = CreatBinaryTree();
	if (BinaryTreeComplete(root))
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
	return 0;
}

二叉树的递归题目就到这里。这几道题好好搞一搞。能很大程度上提高你对于递归的了解。

结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

先搞面包再谈爱

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

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

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

打赏作者

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

抵扣说明:

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

余额充值