二叉树的链式表示(2)

续上节

二叉树的第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题目思路

965.单值二叉树

思路一

将根节点的值分别与左右孩子的值比较,如果相等则继续比较,如果不相等则返回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);
}

100.相同的树

比较两棵树的根节点、左子树和右子树,若三者完全相同,则可判定为同一棵树。

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);
}

递归展开图

101.对称二叉树

思路:创建一个子函数,用来比较左右子树,是同时进行的。(左右两边都得比较)只使用一个参数不能比较。

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);
}

递归展开图

144.二叉树的前序遍历

思路:先遍历树,记录树的节点个数,再前序遍历树,将其节点存储在数组中,打印。

注意:

  1. returnSize 是输出型参数,OJ 系统需要知道数组元素个数才能正确调用数组,因此必须返回数组元素总数。
  2. 在函数调用时,不能直接向 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;
}

递归展开图

572.另一棵树的子树

思路分析:

  1. subRoot 为空树,则空树是任何树的子树,直接返回 true
  2. root 为空而 subRoot 不为空,显然无法匹配,返回 false
  3. 若当前节点值 subRoot->val 等于 root->val,且 isSameTree 验证两树结构相同,则返回 true
  4. 否则递归检查左子树或右子树是否存在匹配的子树。

核心思路是将子树匹配问题分解为两个步骤:

  • 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);

}

前序遍历建立二叉树

实现思路:

  1. 初始化一个数组存储输入序列
  2. 遍历数组处理每个元素:
    • 遇到"#"时返回空节点
    • 遇到字符时创建新节点
  3. 递归构建左右子树
  4. 返回构建完成的二叉树结构
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)的方式,具体步骤如下:

  1. 递归销毁左子树:首先深入访问并销毁当前节点的左子树
  2. 递归销毁右子树:然后深入访问并销毁当前节点的右子树
  3. 销毁根节点:最后处理并销毁当前节点本身

后序遍历之所以适合销毁操作,是因为:

  • 它保证在销毁父节点前,其所有子节点都已被销毁
  • 符合"先处理子问题,再处理父问题"的递归思想
  • 避免了访问已释放内存的风险
cpp 复制代码
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

递归展开图

二叉树的层序遍历

二叉树的层序遍历可以通过队列实现,具体步骤如下:

  1. 初始化队列,存储类型为树节点指针
  2. 将根节点入队
  3. 循环执行以下操作直到队列为空:
    • 出队一个节点并访问
    • 将该节点的左右子节点依次入队

这种方法保证了节点按层级顺序依次被访问。

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);
}

判断完全二叉树

思路:

判断一棵二叉树是否为完全二叉树的核心思路是层序遍历 结合空节点检测。具体实现步骤如下:

  1. 初始化队列:创建一个队列,并将根节点入队。
  2. 开始遍历
    • 从队列中取出队首节点
    • 如果遇到第一个空节点,则标记这个状态
    • 继续检查队列中剩余的节点:
      • 如果后续节点中出现非空节点,则不是完全二叉树
      • 如果后续节点全部为空,则是完全二叉树
  3. 处理非空节点
    • 对于每个非空节点,无论其左右子节点是否为空,都按顺序将它们入队
    • 这样可以保证按层级顺序检查所有节点
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;
}
相关推荐
Tairitsu_H2 小时前
[LC优选算法#2] 滑动窗口 | 长度最小的子数组 | 无重复字符的最长子串 | 最大连续1的个数
算法
小欣加油2 小时前
leetcode3689最大子数组总值I
c++·算法·leetcode·职场和发展·贪心算法
徐寿春2 小时前
什么是数据倾斜
java·guava
下午写HelloWorld2 小时前
【概念与应用】轻量级加密算法LEA、动态脱敏算法DDA、零知识证明ZKP和优化协同交互协议OCIP
算法·区块链·密码学·安全架构·零知识证明
李白的天不白2 小时前
一个服务器可以搭建多个网站
java·tomcat
●VON2 小时前
AtomGit Flutter鸿蒙客户端:共享组件
java·flutter·华为·harmonyos·鸿蒙
程序猿乐锅2 小时前
【JAVASE | 第十七篇】Java 网络通信
java·开发语言
执于代码2 小时前
Java交互打印的问题
java
飞舞哲2 小时前
三维点云最小二乘拟合MATLAB程序
开发语言·算法·matlab