二叉树OJ练习题(C语言版)

目录

 一、相同的树

 二、单值二叉树

 三、对称二叉树

 四、树的遍历

前序遍历

中序遍历

后序遍历

 五、另一颗树的子树

 六、二叉树的遍历

 七、翻转二叉树

 八、平衡二叉树


 一、相同的树

链接:100. 相同的树 - 力扣(LeetCode)

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);
}
  • 首先考虑比较时节点为空的情况,当比较到二者节点都为空时,则当前二者节点相同,返回true。
  • 二者节点只有一个为空时, 当前二者节点不相同,返回false。
  • 然后考虑不为空时,判断二者的值是否相等,不相等返回false。
  • 最后递归调用比较二者当前节点的左子树和右子树,都为true则返回true。

 二、单值二叉树

链接:965. 单值二叉树 - 力扣(LeetCode)

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。
  • 如果左右子树的值都与当前节点的值相同,则递归判断左右子树是否为同值二叉树,即左右子树的所有节点的值都相同。
  • 这个递归过程会一直往下遍历到叶子节点,如果所有节点的值都相同,则返回true,否则返回false。

 三、对称二叉树

思路:左子树的左节点与右子树的右节点比较,左子树的右节点和右子树的左节点比较

bool _isSymmetric(struct TreeNode* leftRoot, struct TreeNode* rightRoot) {
    if (leftRoot == NULL && rightRoot == NULL)
        return true;
    if (leftRoot == NULL || rightRoot == NULL)
        return false;
    if (leftRoot->val != rightRoot->val)
        return false;
    return _isSymmetric(leftRoot->left, rightRoot->right) 
        && _isSymmetric(leftRoot->right, rightRoot->left);
}

bool isSymmetric(struct TreeNode* root) {
    return _isSymmetric(root->left, root->right);
}

OJ是允许我们额外根据需要创建函数的,原函数的参数只有一个,不能满足同时判断两个节点的是否相等的要求,所以我们创建递归函数_isSymmetric(),用来判断左右子树是否对称。

  • 如果左右子树都为空,说明对称,返回true;
  • 如果左右子树只有一个为空,说明不对称,返回false;
  • 如果左右子树的值不相等,说明不对称,返回false;
  • 否则,递归判断左子树的左子树和右子树的右子树是否对称,以及左子树的右子树和右子树的左子树是否对称,如果都对称,返回true,否则返回false。
  • 函数isSymmetric()则是调用_isSymmetric()函数,传入根节点的左子树和右子树,判断整个二叉树是否对称。

 四、树的遍历

前序遍历

链接:144. 二叉树的前序遍历 - 力扣(LeetCode)

int TreeSize(struct TreeNode* root) {
    return root == NULL ? 0 : 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(*returnSize * sizeof(int));
    int i = 0;
    _preorder(root, a, &i);
    return a;
}

题中要求我们将前序遍历的结果存到数组中输出数组,那么我们需要创建一个数组插入数据,同时我们也需要数组的大小,所以我们增加了

  • TreeSize函数统计树的节点个数,
  • _preorder递归函数将节点插入数组。

 preorderTraversal函数中:

  • 首先将TreeSize统计的树的大小赋值给returnSize。
  • 为指针a开辟数组所需的空间,用于存放数组元素。
  • 整型变量 i 用于记录数组当前的索引。
  • 调用_preorder函数对数组进行插入数据。
  • 返回数组a。

 _preorder函数中:

  • 如果当前节点为空,则结束函数。
  • 否则,将当前节点的值插入数组中,每次插入结束后,数组的索引值加一。
  • 这里索引参数为指针类型,用于接收索引 i 的地址,这样才能在函数中及时更新索引值。
  • 递归调用_preorder函数对当前节点的左节点进行处理。
  • 左节点处理后,递归调用_preorder函数对当前节点的右节点进行处理。 

中序遍历

94. 二叉树的中序遍历 - 力扣(LeetCode)

int TreeSize(struct TreeNode* root) {
    return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
void _inorder(struct TreeNode* root, int* a, int* i) {
    if (root == NULL)
        return;
    _inorder(root->left, a, i);
    a[(*i)++] = root->val;
    _inorder(root->right, a, i);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* a = (int*)malloc(*returnSize * sizeof(int));
    int i = 0;
    _inorder(root, a, &i);
    return a;
}

后序遍历

145. 二叉树的后序遍历 - 力扣(LeetCode)

int TreeSize(struct TreeNode* root) {
    return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
void _postorder(struct TreeNode* root, int* a, int* i) {
    if (root == NULL)
        return;
    _postorder(root->left, a, i);
    _postorder(root->right, a, i);
    a[(*i)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* a = (int*)malloc(*returnSize * sizeof(int));
    int i = 0;
    _postorder(root, a, &i);
    return a;
}

五、另一颗树的子树

链接:572. 另一棵树的子树 - 力扣(LeetCode)

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 (isSameTree(root, subRoot))
        return true;
    
    return isSubtree(root->left, subRoot)
        || isSubtree(root->right, subRoot);
}

调用 isSameTree 辅助判断子树是否相等:

  • 首先考虑比较时节点为空的情况,当比较到二者节点都为空时,则当前二者节点相同,返回true。
  • 二者节点只有一个为空时, 当前二者节点不相同,返回false。
  • 然后考虑不为空时,判断二者的值是否相等,不相等返回false。
  • 最后递归调用比较二者当前节点的左子树和右子树,都为true则返回true。

 isSubtree函数:

  • 如果 root 是 NULL,那么它不可能包含任何子树,函数返回 false
  • 如果 isSameTree(root, subRoot) 返回 true,说明在当前的节点上,root 和 subRoot 完全相同,那么 subRoot 当然是 root 的子树,函数返回 true
  • 如果当前节点不匹配,那么递归地在 root 的左子树和右子树中查找 subRoot
  • 如果 subRoot 是 root 左子树或右子树的子树,函数就返回 true

 六、二叉树的遍历

链接:二叉树遍历_牛客题霸_牛客网 (nowcoder.com) 

我们需要根据给出的先序(前序)遍历反推出树的样子,例如下图,我们可以动手试一试:

好的,如果你已经熟悉如何根据遍历反推二叉树的形状,那么试着推出题中的遍历的吧。

 注意:这道题需要写出主函数,并不像力扣提供接口。

首先我需要创建二叉树的结构体和相关函数:

#include <stdio.h>
#include <stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
    BTDataType data;
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
    BTNode* node=(BTNode*)malloc(sizeof(BTNode));
    if(node==NULL){
        perror("malloc fail");
        return NULL;
    }
    node->data=x;
    node->left=node->right=NULL;
    return node;
}

然后进行二叉树的创建:

  • 该函数接收两个参数,一个是字符串a,另一个是指向整型变量i的指针,用于记录当前处理到字符串a的哪个位置。
  • 当前位置数组元素为 # 表示元素值为空,更新数组索引到下一个位置,返回NULL
  • 当前位置元素不为空则为其开辟空间,为树创建新节点,更新数组索引到下一个位置。
  • 递归调用CreateTree函数分别创建该节点的左右子树。
  • 最后返回该节点的指针。

在main函数中:

  • 先定义了一个字符数组a,用于存储输入的字符串,
  • 然后调用CreateTree函数创建二叉树,将根节点的指针赋值给root,
  • 最后调用InOrder函数对二叉树进行中序遍历,并输出换行符。
BTNode* CreateTree(char* a,int* i)
{
    if(a[*i]=='#'){
        (*i)++;
        return NULL;
    }
    BTNode* root=BuyNode(a[*i]);
    (*i)++;
    root->left=CreateTree(a, i);
    root->right=CreateTree(a, i);
    return root;
}
void InOrder(BTNode* root)
{
    if(root==NULL)
        return;
    InOrder(root->left);
    printf("%c ",root->data);
    InOrder(root->right);
}
int main() {
    char a[1000];
    scanf("%s",a);
    int i=0;
    BTNode* root=CreateTree(a,&i);
    InOrder(root);
    printf("\n");
    return 0;
}

 七、翻转二叉树

链接:226. 翻转二叉树 - 力扣(LeetCode)

truct TreeNode* invertTree(struct TreeNode* root) {
    if(root==NULL)
        return NULL;
    struct TreeNode* left=invertTree(root->left);
    struct TreeNode* right=invertTree(root->right);
    root->left=right;
    root->right=left;
    return root;
}
  •  如果当前节点为空,则返回NULL.
  • 否则,递归调用函数对左右子树进行处理,递归到二叉树最底部,从最底部往上进行翻转。
  • 将节点的左节点赋值为右节点,右节点赋值为左节点,
  • 最后返回根节点。

八、平衡二叉树

链接:110. 平衡二叉树 - 力扣(LeetCode)

第一种:求出左右子树高度进行判断比较。

  • height函数用于计算二叉树的高度,它采用递归的方式计算左右子树的高度,然后返回左右子树高度的较大值加1,表示当前节点的高度。

  • isBalanced函数则是用于判断二叉树是否平衡,它首先判断当前节点是否为空,如果为空则返回true,否则通过 abs 函数计算左右子树的高度差的绝对值,如果高度差大于1,则返回false,否则递归判断左右子树是否平衡。

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

bool isBalanced(struct TreeNode* root) {
    if (root == NULL) {
        return true;
    }
    int leftHeight = height(root->left);
    int rightHeight = height(root->right);
    if (abs(leftHeight - rightHeight) > 1) {
        return false;
    }
    return isBalanced(root->left) && isBalanced(root->right);
}

 第二种与第一种功能方法一样,但这种方式更简洁

同样首先写出求树高度的函数,但这里使用三目运算符使函数更简洁。(fmax返回两个参数中较大的那个。)

int maxDepth(struct TreeNode* root){
    return root ? 1 + fmax(maxDepth(root->left) , maxDepth(root->right)) : 0;      
}

bool isBalanced(struct TreeNode* root){
    if(root == NULL)
        return true;
    int left = maxDepth(root->left);
    int right = maxDepth(root->right);
    return abs(left - right) < 2
        && isBalanced(root->left)
        && isBalanced(root->right);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/121226.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

社区治理进化史!拓世法宝化身“虚拟社工”,点亮智能社区的每一个角落

时光流转、技术猛进&#xff0c;社区不再只是在制度层面作为城市治理的最小单元&#xff0c;更是在民生层面成为政府联系、服务群众的“神经末梢”。城市的脚步越来越匆忙&#xff0c;人们对于社区的服务期待也愈发高涨。面对日益复杂的社区治理和服务需求&#xff0c;我们迫切…

蓝桥杯:分数

题目 思路 等比数列求和&#xff0c;手算然后输出 代码&#xff08;已过&#xff09; #include <iostream> using namespace std; int main() {// 请在此输入您的代码int a1024*1024-1;int b1024*512;cout<<a<<"/"<<b;return 0; }

eclipse的安装与配置详细教程(包括UML插件 汉化 JDK 代码补全 导入导出等)

Eclipse安装与配置详细教程 1.Eclipse安装与配置 1.将JDK与Eclipse这两个软件安装包放在一个文件夹下&#xff0c;方便之后安装使用。 2.安装JDK 在D&#xff1a;LeStoreDownload\Java文件夹下另外新建三个文件夹分别命名为java、jdk和eclipse&#xff08;分别用于Java、jdk…

关于unity中 编辑器相关逻辑的记录

prefab 在场景中 , 用这个方法可以获取它的磁盘路径: [MenuItem("Gq_Tools/↓获取prefab路径")] public static void SaveDecalParameters() { var objs Selection.objects; var obj objs[0] as GameObject; Object parentObject Prefab…

SAP 开发查找增强程序

参考文章https://blog.csdn.net/SAPmatinal/article/details/129987722?ops_request_misc%257B%2522request%255Fid%2522%253A%2522169949816116800225559526%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id16994981611680022555…

关于视频封装格式和视频编码格式的简介

文章目录 简介视频封装格式&#xff08;Video Container Format&#xff09;视频编码格式&#xff08;Video Compression Format&#xff09;两者关系总结webm 格式简介webm视频编码格式webm音频编码格式webm总结 简介 视频封装格式&#xff08;Video Container Format&#x…

2023年【起重机司机(限门式起重机)】新版试题及起重机司机(限门式起重机)找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机司机(限门式起重机)新版试题考前必练&#xff01;安全生产模拟考试一点通每个月更新起重机司机(限门式起重机)找解析题目及答案&#xff01;多做几遍&#xff0c;其实通过起重机司机(限门式起重机)作业模拟考试…

另辟蹊径者 PoseiSwap:背靠潜力叙事,构建 DeFi 理想国

前不久&#xff0c;灰度在与 SEC 就关于 ETF 受理的诉讼案件中&#xff0c;以灰度胜诉告终。灰度的胜利&#xff0c;也被加密行业看做是加密 ETF 在北美地区阶段性的胜利&#xff0c; 该事件也带动了加密市场的新一轮复苏。 此前&#xff0c;Nason Smart Money 曾对加密市场在 …

大语言模型(LLM)综述(七):大语言模型设计应用与未来方向

A Survey of Large Language Models 前言8 A PRACTICAL GUIDEBOOK OF PROMPT DESIGN8.1 提示创建8.2 结果与分析 9 APPLICATIONS10 CONCLUSION AND FUTURE DIRECTIONS 前言 随着人工智能和机器学习领域的迅速发展&#xff0c;语言模型已经从简单的词袋模型&#xff08;Bag-of-…

攻防世界题目练习——Web引导模式(四)(持续更新)

题目目录 1. shrine2. very_easy_sql3. fakebook 1. shrine 打开网页题目内容如下&#xff1a; 是一段代码&#xff0c;我们把它还原一下&#xff1a; import flask import osapp flask.Flask(__name__) app.config[FLAG] os.environ.pop(FLAG) #这里应该是将config配置里…

Linux/centos上如何配置管理Web服务器?

Linux/centos上如何配置管理Web服务器&#xff1f; 1 Web简单了解2 关于Apache3 如何安装Apache服务器&#xff1f;3.1 Apache服务安装3.2 httpd服务的基本操作 4 如何配置Apache服务器&#xff1f;4.1 关于httpd.conf配置4.2 常用指令 5 简单实例 1 Web简单了解 Web服务器称为…

一篇文章带你全面了解智能地面水处理一体机

一、智能地面水处理一体机 1、设备外壳常规尺寸有&#xff1a;1630*760*560&#xff08;mm&#xff09;&#xff1b;1630*900*560&#xff08;mm&#xff09; 2、外壳有不锈钢、碳钢材质 二、产品构成&#xff08;电气控制柜雨水过滤、消毒处理机&#xff09; 1. 上半部为雨…

【慢SQL性能优化】 一条SQL的生命周期 | 京东物流技术团队

一、 一条简单SQL在MySQL执行过程 一张简单的图说明下&#xff0c;MySQL架构有哪些组件和组建间关系&#xff0c;接下来给大家用SQL语句分析 例如如下SQL语句 SELECT department_id FROM employee WHERE name Lucy AND age > 18 GROUP BY department_id其中name为索引&a…

k8s 目录和文件挂载到宿主机

k8s生产中常用的volumes挂载方式有&#xff1a;hostPath、pv&#xff0c;pvc、nfs 1.hostPath挂载 hostPath是将主机节点文件系统上的文件或目录挂载到Pod 中&#xff0c;同时pod中的目录或者文件也会实时存在宿主机上&#xff0c;如果pod删除&#xff0c;hostpath中的文…

线性代数 | 矩阵运算 加减 数乘 矩阵的幂运算

文章目录 1. 矩阵加减和数乘2.矩阵与矩阵的乘法2.1相乘条件&#xff1a;看中间&#xff0c;取两头2.2 相乘计算方法 3. 矩阵的幂3.1 观察归纳法3.2 邻项相消法3.3 化为对角 4.矩阵求逆&#xff08;除法&#xff09;4.1 判断是否可逆&#xff08;证明题或者要求求出逆矩阵&#…

centos 7部署Mysql8.0主从

Mysql官网中关于部署主从的网址 环境准备&#xff1a; 搭建虚拟机和安装Mysql之前的文章中已经涉及&#xff0c;在此不再赘述。 主从IPMysql账号密码主192.168.213.4root/Root1234!从192.168.213.5root/Root1234! 1、主数据库设置 配置my.cnf 一般存放于/etc/。 主从配…

VSCode修改主题为Eclipse 绿色护眼模式

前言 从参加开发以来&#xff0c;一直使用eclipse进行开发&#xff0c;基本官方出新版本&#xff0c;我都会更新。后来出来很多其他的IDE工具&#xff0c;我也尝试了&#xff0c;但他们的主题都把我劝退了&#xff0c;黑色主题是谁想出来&#xff1f;&#x1f602; 字体小的时…

【原创分享】LDO电源PCB设计要点

LDO模块是Low Drop-Out的缩写&#xff0c;也称为低压差稳压器。它是一种电子组件&#xff0c;主要用于将高电压降至较低电压&#xff0c;并提供稳定的电源供应。LDO模块通常由一个直流电压调节器和一个电流放大器组成。其工作原理是通过在输入和输出之间创建一个稳定的电压差&a…

Linux 中断实验

一.什么是中断 中断是指 CPU 在执行程序的过程中&#xff0c;出现了某些突发事件急待处理&#xff0c; CPU 必须暂停当前程序的执行&#xff0c;转去处理突发事件&#xff0c;处理完毕后又返回原程序被中断的位置继续执行。由于中断的存在极大的提高了 CPU 的运行效率&#x…

拓展认知边界:如何给大语言模型添加额外的知识

Integrating Knowledge in Language Models P.s.这篇文章大部分内容来自Stanford CS224N这门课Integrating Knowledge in Language Models这一节&#x1f601; 为什么需要给语言模型添加额外的知识 1.语言模型会输出看似make sense但实际上不符合事实的内容 语言模型在生成…