二叉树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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2022年09月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 十六进制数100&#xff0c;对应的十进制数为 &#xff1f;&#xff08; &#xff09; A: 128 B: 256 C: 28 D: 56 答…

【多线程】Lambda表达式

package org.example;public class TestLambda {public static void main(String[] args) {Like likenew Like();like.lambda();}}//定义一个函数式接口 interface ILike{void lambda(); }//实现类 class Like implements ILike{Overridepublic void lambda() {System.out.prin…

Java中的static

目录 static修饰成员变量 静态成员变量特征 static修饰成员方法 【静态方法特性】 static成员变量初始化 就地初始化 静态代码块初始化 注意事项 static修饰成员变量 静态成员变量特征 static修饰的成员变量&#xff0c;称为静态成员变量&#xff0c;静态成员变量最大的…

前馈神经网络自动梯度计算和预定义算子

目录 1 自动梯度计算和预定义算子 1.1 利用预定义算子重新实现前馈神经网络 1.2 完善Runner类 1.3 模型训练 1.4 性能评价 1.5 增加一个3个神经元的隐藏层&#xff0c;再次实现二分类&#xff0c;并与1.1.1做对比. 1.6 自定义隐藏层层数和每个隐藏层中的神经元个数&#xf…

【Python工具】Panoply介绍及安装步骤

Panoply介绍及安装步骤 1 Panoply介绍2 Panoply安装步骤&#xff08;Windows&#xff09;2.1 下载并安装JAVA环境2.2 下载Panoply报错&#xff1a;Error: A JNI error has occurred, please check your installation and try again. 参考 1 Panoply介绍 Panoply是一款由美国国…

Pycharm安装配置Pyqt5教程(保姆级)

目录 一、前言 1、依赖包 2、工具 二、安装依赖包 三、配置环境 四、配置设计工具 1、Qt Designer 2、PyRcc 3、PyUIC 五、使用 1、界面设计 2、ui文件转化为py文件 一、前言 很多情况下需要为程序设计一个GUI界面&#xff0c;在Python中使用较多的用户界面设计工具…

深度学习之基于Python+OpenCV+dlib的考生信息人脸识别系统(GUI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习在人脸识别领域的应用已经取得了显著的进展。Python是一种常用的编程语言&#xff0c;它提供了许多强大的库…

Makefile初识

目录 0.前期准备0.1、程序编译链接&#xff1a; 1.Makefile基础1.1、认识Makefile1.2、Makefile定义模式&#xff1a;(1) 定义模式&#xff1a;(2) 执行Makefile&#xff1a; 1.3、Makefile的变量(1) 变量定义&#xff1a;(2) **变量的赋值符**:(3) 自动化变量 1.4 伪目标1.5 文…

2022年12月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 列表L1中全是整数&#xff0c;小明想将其中所有奇数都增加1&#xff0c;偶数不变&#xff0c;于是编写了如下图所示的代…

gorm的自动化工具gen

gorm的自动化工具gen 官方 https://gorm.io/zh_CN/gen/假设数据库结构如 这里使用gen-tool 安装 go install gorm.io/gen/tools/gentoollatest用法 gentool -hUsage of gentool:-c string配置文件名、默认值 “”、命令行选项的优先级高于配置文件。 -db string指定Driver…

volatile-禁重排案例详解

在每一个volatile写操作前面插入一个StoreStore屏障--->StoreStore屏障可以保证在volatile写之前&#xff0c;其前面所有的普通写操作都已经刷新到主内存中。 在每一个volatile写操作后面插入一个StoreLoad屏障--->StoreLoad屏障的作用是避免volatile写与后面可能有的vo…

Python:PDF转长图像和分页图像

简介&#xff1a;随着电子化文档的普及&#xff0c;PDF文件的使用频率越来越高。有时我们需要将PDF中的内容转化为图片格式进行分享或编辑&#xff0c;那么如何才能轻松地完成此任务呢&#xff1f;本文将为你展示一个Python工具&#xff1a;如何将PDF文件转化为图片&#xff0c…

【黑马程序员】Maven 进阶

文章目录 前言一、分模块开发与设计1. 分模块开发意义2. 分模块开发&#xff08;模块拆分&#xff09;2.1 创建 Maven 模块2.2 书写模块代码2.3 通过 Maven 指令安装模块到本地仓库&#xff08;install 指令&#xff09; 二、依赖管理1. 依赖传递1.1 依赖传递冲突问题 2. 可选依…

Rust编程基础核心之所有权(上)

1.什么是所有权? Rust 的核心功能&#xff08;之一&#xff09;是 所有权&#xff08;ownership&#xff09;。虽然该功能很容易解释&#xff0c;但它对语言的其他部分有着深刻的影响。 所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制&#x…

【Linux】第十二站:进程

文章目录 1.windows和linux中的进程2.先描述3.在组织4.具体的Linux系统是如何做的&#xff1f;1.基本概念2.描述进程-PCB3.task_struct和PCB的关系4.task_struct内容分类5.linux具体如何做的&#xff1f;6.查看进程 1.windows和linux中的进程 一个已经加载到内存的程序&#xf…

【MySQL】MVCC机制(undo log,read view)

文章目录 前言一. 预备知识二. 模拟MVCC三. Read View四. RC与RR的本质区别结束语 前言 MVCC&#xff08;多版本并发控制&#xff09;是一种用来解决读-写冲突的无锁并发控制 MVCC为事务分配单向增长的事务ID&#xff0c;为每个修改保存一个版本&#xff0c;版本与事物ID相关联…

全球首款双模型AI手机METAVERTU2,为用户开发“第二大脑”

在2023年11月1日&#xff0c;英国奢侈手机品牌VERTU在香港举办了一场新品发布会&#xff0c;它推出了一款全新的AI手机称为METAVERTU2&#xff0c;这是全球首款双模型AI手机。此款手机将Web3技术与人工智能相结合&#xff0c;通过AI模型标记数据和AI Agent的方式&#xff0c;将…

如何设置OBS虚拟摄像头给钉钉视频会议使用

环境&#xff1a; OBS Studio 29.1.3 Win10 专业版 钉钉7.1.0 问题描述&#xff1a; 如何设置OBS虚拟摄像头给钉钉视频会议使用 解决方案&#xff1a; 1.打开OBS 底下来源这添加视频采集设备 选择OBS虚拟摄像头 2.源那再建一个图像&#xff0c;随便选一张图片 3.点击虚…

SpringBoot项目打包与运行

1.clean生命周期 说明&#xff1a;为了项目能够正确打包&#xff0c;先清理打包文件。 2.package生命周期 说明&#xff1a;打包后生成以下目录。 2.1问题 说明&#xff1a;springboot_08_ssmp-0.0.1-SNAPSHOT.jar中没有主清单属性。 2.2解决 说明&#xff1a;注释skip&…

Python之Excel数据相关

Excel Microsoft Excel是Microsoft为使用Windows和Apple Macintosh操作系统的电脑编写的一款电子表格软件。直观的界面、出色的计算功能和图表工具&#xff0c;再加上成功的市场营销&#xff0c;使Excel成为最流行的个人计算机数据处理软件。在1993年&#xff0c;作为Microsof…