续上节
二叉树的第k层节点个数
1.思路
要计算第k层的节点数,可以转化为计算其左右子树的第k-1层节点数之和。具体来说:
- 第1层的k层节点数 = 第2层的k-1层节点数
- 第2层的k-1层节点数 = 第3层的k-2层节点数
- 依此类推,直到递归到k=1时直接返回当前层的节点数

2.代码
cpp
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL||k<=0)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
3.递归展开图

查找二叉树中的某个节点
思路:如果根为空,则返回,如果该节点数据等于要查找的数据,则返回该节点的地址,否则继续遍历左右子树,直到查找完毕。要用变量储存查找结果,否则会多次重复调用。
cpp
// 二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) //错误原因,没有将返回结果记录下来,导致即使找到了也返回NULL
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode* left = BinaryTreeFind(root->left, x);
if (left)
{
return left;
}
BTNode* right = BinaryTreeFind(root->right, x);
if (right)
{
return right;
}
return NULL;
}
递归展开图

一些LeeCode题目思路
思路一
将根节点的值分别与左右孩子的值比较,如果相等则继续比较,如果不相等则返回false,最后返回比较结果。
cpp
bool isUnivalTree(struct TreeNode* root) {
if(root == NULL)
{
return true;
}
if(root->left && root->val != root->left->val)
{
return false;
}
if(root->right && root->val != root->right->val)
{
return false;
}
return isUnivalTree(root->left)&&isUnivalTree(root->right);
}
递归展开图

思路二
传入一个值与其比较,遍历这棵树
cpp
bool _isUnivalTree(struct TreeNode* root,int n) {
if(root==NULL)
{
return true;
}
if(root->left&&root->left->val!=n)
{
return false;
}
if(root->right&&root->right->val!=n)
{
return false;
}
return _isUnivalTree(root->left, n)&&_isUnivalTree(root->right, n);
}
bool isUnivalTree(struct TreeNode* root) {
return _isUnivalTree(root,root->val);
}
比较两棵树的根节点、左子树和右子树,若三者完全相同,则可判定为同一棵树。
cpp
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);
}
递归展开图

思路:创建一个子函数,用来比较左右子树,是同时进行的。(左右两边都得比较)只使用一个参数不能比较。
cpp
bool _isSymmetric(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 _isSymmetric(p->left,q->right)&&_isSymmetric(p->right,q->left);
}
bool isSymmetric(struct TreeNode* root) {
return _isSymmetric(root->left,root->right);
}
递归展开图

思路:先遍历树,记录树的节点个数,再前序遍历树,将其节点存储在数组中,打印。
注意:
- returnSize 是输出型参数,OJ 系统需要知道数组元素个数才能正确调用数组,因此必须返回数组元素总数。
- 在函数调用时,不能直接向 preOrder() 传递普通变量 i,因为形参的修改不会影响实参,必须传递变量地址。
cpp
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* pi)
{
if(root==NULL)
{
return;
}
a[(*pi)++]=root->val;
preOrder(root->left,a,pi);
preOrder(root->right,a,pi);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = TreeSize(root);
int* a = (int*)malloc(sizeof(struct TreeNode)*(*returnSize));
int i=0;
preOrder(root,a,&i);
return a;
}
递归展开图

思路分析:
- 若
subRoot为空树,则空树是任何树的子树,直接返回true; - 若
root为空而subRoot不为空,显然无法匹配,返回false; - 若当前节点值
subRoot->val等于root->val,且isSameTree验证两树结构相同,则返回true; - 否则递归检查左子树或右子树是否存在匹配的子树。
核心思路是将子树匹配问题分解为两个步骤:
- 在
root中定位与subRoot根节点值相同的节点(类似查找); - 通过
isSameTree验证该节点下的子树结构是否完全一致。
注:子树匹配可视为在更高层级上进行的节点值匹配与结构验证的结合。
cpp
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(subRoot==NULL)
{
return true;
}
if(root==NULL)
{
return false;
}
if(root->val==subRoot->val&&isSameTree(root,subRoot))
{
return true;
}
return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}
实现思路:
- 初始化一个数组存储输入序列
- 遍历数组处理每个元素:
- 遇到"#"时返回空节点
- 遇到字符时创建新节点
- 递归构建左右子树
- 返回构建完成的二叉树结构

cpp
#include <stdio.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
BTNode* CreateTree(char* a,int* pi)
{
if(a[*pi]=='#')
{
(*pi)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
root->data = a[(*pi)++];
root->left = CreateTree(a, pi);
root->right = CreateTree(a,pi);
return root;
}
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
int main() {
char a[100];
scanf("%s",a);
int i=0;
BTNode* root = CreateTree(a,&i);
InOrder(root);
return 0;
}
二叉树的销毁
二叉树的销毁通常采用后序遍历(post-order traversal)的方式,具体步骤如下:
- 递归销毁左子树:首先深入访问并销毁当前节点的左子树
- 递归销毁右子树:然后深入访问并销毁当前节点的右子树
- 销毁根节点:最后处理并销毁当前节点本身
后序遍历之所以适合销毁操作,是因为:
- 它保证在销毁父节点前,其所有子节点都已被销毁
- 符合"先处理子问题,再处理父问题"的递归思想
- 避免了访问已释放内存的风险
cpp
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreeDestory(root->left);
BinaryTreeDestory(root->right);
free(root);
}
递归展开图

二叉树的层序遍历
二叉树的层序遍历可以通过队列实现,具体步骤如下:
- 初始化队列,存储类型为树节点指针
- 将根节点入队
- 循环执行以下操作直到队列为空:
- 出队一个节点并访问
- 将该节点的左右子节点依次入队
这种方法保证了节点按层级顺序依次被访问。
cpp
// 层序遍历
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
printf("%d ", front->data);
QueuePop(&q);
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
QueueDesTroy(&q);
}
判断完全二叉树
思路:
判断一棵二叉树是否为完全二叉树的核心思路是层序遍历 结合空节点检测。具体实现步骤如下:
- 初始化队列:创建一个队列,并将根节点入队。
- 开始遍历 :
- 从队列中取出队首节点
- 如果遇到第一个空节点,则标记这个状态
- 继续检查队列中剩余的节点:
- 如果后续节点中出现非空节点,则不是完全二叉树
- 如果后续节点全部为空,则是完全二叉树
- 处理非空节点 :
- 对于每个非空节点,无论其左右子节点是否为空,都按顺序将它们入队
- 这样可以保证按层级顺序检查所有节点
cpp
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)//遇到第一个空出来判断
{
break;
}
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front) //如果有非空,就不是完全二叉树
{
QueueDesTroy(&q);
return false;
}
}
QueueDesTroy(&q);
return true;
}