1.查找二叉树第k层节点个数
前情提要:
c
typedef int BTDataType;
typedef struct bynode
{
struct bynode* left;
struct bynode* right;
BTDataType data;
}BTNode;
c
int TreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
函数功能:
给定一个二叉树的根节点 root 和一个整数 k,返回该二叉树第 k 层的节点个数。规定根节点为第1层。
递归思路:
-
如果当前节点为空,则不可能有任何节点,返回0。
-
如果 k == 1,说明当前层就是我们要找的层,当前节点本身算一个,返回1。
-
否则,问题转化为:求左子树的第 k-1 层节点个数 + 右子树的第 k-1 层节点个数。
这样通过递归,每次下降一层,直到到达目标层。
如果看不明白,我们还可以先来画图分析一波。
假如查找k==3的这个位置节点个数

- k==3,1->2
- k==2,2->3
- k==1,return 1,3->2
- 2->NULL,return 0,计算2的左右返回值为1+0,所以return 1 给节点1
- 2->1,此时计算出1的左节点为1,同理计算1的右子树
- ...
关键点理解
-
递归的深度:每次递归调用,k减1,直到k=1时返回当前节点计数。这实际上就是在向下走k-1层。
-
空节点处理:遇到空节点直接返回0,表示该分支没有节点。
-
分治思想:将问题分解为左右子树的子问题,最后合并结果。
2.二叉树查找值为x的节点
c
BTNode* TreeFind(BTNode* root, BTDataType x)

那有了前面的讲解,此时是不是感觉这题就比较简单呀,查找值为x的节点,这还不简单?
如同前面一样,当root==NULL时,返回NULL,当找到这个相等的值时,直接返回root,最后递归遍历是不是就好了呀!是不是感觉太简单了?
但事实真的是如此吗?我们来写一下尝试一下:
c
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
TreeFind(root->left, x);
TreeFind(root->right, x);
}
我们看,这是不是就写完了呀?但事实真的就是这样吗?
好了,在这里我们就不再卖关子了,其实这段代码是一个非常经典的错误,这个错误是在哪里呢?我们来看一看:
我们来想一想,在递归中,当if (root->data == x) return root;找到了时,这个root返回到哪里去了,是直接返回出去,还是返回到上一个递归的函数呢?
我想大家都知道这个答案了吧,而且还有第二个问题,我们如果两个if语句都没进去,那这里是不是就没有返回值了呀,而这个函数要求返回BTNode* ,那这里是不是就出现大问题了呀,好了,我们来整理一下上述可能发生的问题:
缺少返回值:
- 在递归调用 TreeFind(root->left, x); 和 TreeFind(root->right, x); 之后,函数没有返回任何值。这导致当程序执行到函数末尾时,没有 return 语句,行为未定义。在C语言中,非 void 函数必须返回一个值,否则可能返回随机值或导致程序崩溃。
递归结果被丢弃:
- 即使左子树或右子树中找到了节点,递归调用的返回值没有被捕获和使用。因此,即使找到了节点,函数也会继续执行后续代码,最终可能返回一个未定义的值。
逻辑不完整
- 正确的查找逻辑应该是:先检查当前节点,若找到则返回;否则在左子树中查找,如果左子树返回非空则返回该结果;否则在右子树中查找,并返回右子树的结果;若左右子树都没找到,则返回 NULL。
那正确写法是不是应该这样呀:
c
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* ret1 = TreeFind(root->left, x);
if (ret1)
return ret1;
BTNode* ret2 = TreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
总结:
务必记住:在递归函数中,每次递归调用的返回值都需要被正确处理。要么直接返回,要么根据结果决定下一步。永远不要忽略递归调用的返回值,除非函数是 void 类型。此外,确保函数的所有分支都有明确的 return 语句。
3.单值二叉树
https://leetcode.cn/problems/univalued-binary-tree/description/

c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isUnivalTree(struct TreeNode* root) {
if (root == NULL)
return true;
if(root->left && root->left->val!=root->val)
return false;
if(root->right && root->right->val!=root->val)
return false;
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
解题思路:
我们可以用递归来检查:
-
如果当前节点为空,则它不影响结果,返回 true。
-
如果当前节点有左孩子,并且左孩子的值与当前节点值不同,则直接返回 false。
-
如果当前节点有右孩子,并且右孩子的值与当前节点值不同,也返回 false。
-
如果当前节点与左右孩子值都相等(或没有孩子),则递归检查左子树和右子树是否也是单值二叉树。
-
只有左右子树都返回 true,整棵树才是单值。
4.相同的树
https://leetcode.cn/problems/same-tree/description/

c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
if(p == NULL && q == NULL)
return true;
if(p == NULL || q == NULL)
return false;
if(p->val != q->val)
return false;
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
递归思路:
-
如果两个节点都为空,则它们相同。
-
如果其中一个为空而另一个不为空,则结构不同,直接返回 false。
-
如果两个都不为空,则比较它们的值,若值不同则返回 false。
-
若值相同,则递归比较它们的左子树和右子树是否分别相同。只有左右子树都相同时,整棵树才相同。
5.对称二叉树
https://leetcode.cn/problems/symmetric-tree/description/

c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isMirror(struct TreeNode* p, struct TreeNode* q)
{
if(p == NULL && q == NULL)
return true;
if(p == NULL || q == NULL)
return false;
return (p->val == q->val)
&& isMirror(p->left, q->right)
&& isMirror(p->right, q->left);
}
bool isSymmetric(struct TreeNode* root) {
if(root == NULL)
return true;
return isMirror(root->left,root->right);
}
解题思路:
一棵树对称意味着:
- 根节点的左子树和右子树互为镜像。
两棵子树互为镜像的条件是:
-
它们的根节点值相等。
-
一棵树的左子树与另一棵树的右子树镜像对称。
-
一棵树的右子树与另一棵树的左子树镜像对称。
这形成了递归定义。
6.另一颗树的子树
https://leetcode.cn/problems/subtree-of-another-tree/submissions/708473483/

c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
if(p == NULL && q == NULL)
return true;
if(p == NULL || q == NULL)
return false;
if(p->val != q->val)
return false;
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) {
if(root == NULL)
return false;
if(subRoot == NULL)
return true;
// 检查当前节点为根的树是否与 subRoot 相同
if (isSameTree(root, subRoot))
return true;
// 否则递归检查左子树和右子树
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
解题思路:
我们需要遍历 root 的每个节点,检查以该节点为根的子树是否与 subRoot 完全相同。这可以用递归实现:
- 先编写一个辅助函数 isSameTree(p, q),判断两棵树是否完全相同
- 在主函数 isSubtree 中:
-
如果 root 为空,则不可能包含非空子树,返回 false。
-
如果 subRoot 为空,根据定义,空树是任何树的子树,返回 true(但通常题目中 subRoot 非空,这里为了严谨可加)。
-
否则,先检查当前 root 和 subRoot 是否相同,若相同则返回 true。
-
如果不同,则递归检查 root 的左子树或右子树是否包含 subRoot,只要一边找到就返回 true。
7.二叉树的前序遍历
https://leetcode.cn/problems/binary-tree-preorder-traversal/description/

c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int treeSize(struct TreeNode* root) {
if (root == NULL)
return 0;
return treeSize(root->left) + treeSize(root->right) + 1;
}
void preorder(struct TreeNode* root,int* a,int* i)
{
if(root == NULL)
{
return;
}
a[(*i)++] = root->val;
preorder(root->left,a,i);
preorder(root->right,a,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = treeSize(root);
int* a=(int*)malloc(sizeof(int)*(*returnSize));
int i = 0;
preorder(root,a,&i);
return a;
}
看似代码不起眼,实则在这里稍不留神,就会有坑哦。
首先,题目要求有malloc开辟节点空间,那么我们是不是要知道开辟多少个呀,所以再用一次我们的treeSize函数来求一下节点个数,开辟出相应的空间。
然后接下来会有一个大坑,为什么我们这里要&i呀,直接传i不行吗???
如果你直接传,那你就掉入陷阱里了,我们在递归调用函数时,如果传i而不是&i,那么i就会一直被重置而不会真正的改变数值,所以在这里我们需要去传指针。
8.二叉树前序遍历
https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef

c
#include <stdio.h>
#include <stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
BTNode* CreateTree(char* n,int* i)
{
if(n[*i] =='#')
{
(*i)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
root->data = n[(*i)++];
root->left = CreateTree(n, i);
root->right = CreateTree(n, i);
return root;
}
int main() {
char n[100];
scanf("%s",n);
int i = 0;
BTNode* root = CreateTree(n,&i);
InOrder(root);
return 0;
}
代码逻辑与思路:
中序遍历函数 InOrder:
-
递归实现:先遍历左子树,然后打印节点值(使用 %c 格式,所以输出字符),再遍历右子树。
-
如果节点为空,直接返回。
构建二叉树函数 CreateTree:
-
参数:字符串指针 n,以及一个整型指针 i(用于在递归中共享当前处理的位置)。
-
首先判断当前字符 n[*i] 是否为 '#':
-
如果是,则 (*i)++ 跳过这个 #,并返回 NULL 表示空节点。
否则,创建一个新节点:
-
分配内存。
-
将当前字符赋值给节点数据,并且 (*i)++ 后移。
-
递归构建左子树,传入相同的 i。
-
递归构建右子树,传入相同的 i。
-
返回根节点。
主函数:
-
定义字符数组 n 存储输入字符串。
-
用 scanf("%s", n) 读取字符串(注意:遇到空格会停止,但输入中可能没有空格,只有字符和#)。
-
初始化索引 i = 0。
-
调用 CreateTree 构建树,传入 &i 以便在递归中更新索引。
-
然后调用 InOrder 进行中序遍历并输出。
易错点与注意事项:
- 索引必须传指针
- 在递归构建树时,所有递归调用必须共享同一个索引变量。如果直接传 i(值传递),每一层递归都会拥有独立的副本,导致索引无法正确更新。阱。
- 处理 '#' 的顺序
- 先判断当前字符是否为 '#',再 (*i)++。如果先自增再判断,就会跳过当前字符而误判。