二叉树经典OJ练习

news2025/1/16 14:41:08

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

二叉树经典OJ练习

收录于专栏【数据结构初阶】
本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

前置说明 

1. 单值二叉树

2. 相同的树

3. 对称二叉树

4. 二叉树的前序遍历

5. 二叉树中序遍历

6. 二叉树的后序遍历

7. 另一颗树的子树


前置说明 

这里大家需要有二叉树的一些基础,还不是很了解的宝子们可以点击下方链接进行查看

---数据结构之二叉树的超详细讲解(3)--(二叉树的遍历和操作)-CSDN博客

1. 单值二叉树

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

题目描述:

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。

只有给定的树是单值二叉树时,才返回 true;否则返回 false

示例 1:

输入:[1,1,1,1,1,null,1]
输出:true

示例 2:

输入:[2,2,2,5,2]
输出:false

提示:

  1. 给定树的节点数范围是 [1, 100]
  2. 每个节点的值都是整数,范围为 [0, 99] 。

分析:

题目中给定树的节点数范围是 [1, 100],这里我们就不需要额外判断空节点的情况,我们只需要对二叉树进行遍历一遍,如果遇到空节点返回true,如果root左节点或右节点val不等于root->val,返回false,将最后的结果上传,即可得到最终的结果.

代码展示: 

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

核心逻辑

  1. 空树检查

    if(root == NULL) return true;

    这里首先检查根节点是否为空。如果根节点为空,则该树被认为是单值二叉树,返回 true

  2. 左子节点值检查

    if(root->left && root->left->val != root->val) return false;

    接下来检查左子节点。如果左子节点存在且其值与根节点的值不相同,则该树不是单值二叉树,返回 false

  3. 右子节点值检查

    if(root->right && root->right->val != root->val) return false;

    同样地,检查右子节点。如果右子节点存在且其值与根节点的值不相同,则该树不是单值二叉树,返回 false

  4. 递归检查子树

    return isUnivalTree(root->left) && isUnivalTree(root->right);

    如果当前节点及其直接子节点都满足条件,则递归检查左子树和右子树。只有在左右子树都是单值二叉树的情况下,当前树才是单值二叉树。

总结

该函数通过递归检查每个节点及其子节点的值是否与根节点的值相同来判断整个二叉树是否是单值二叉树。如果某个节点的值与其父节点不同,立即返回 false。否则,继续递归检查其子树,最终返回整个树的结果。

2. 相同的树

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

题目描述:

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:p = [1,2,3], q = [1,2,3]
输出:true

示例 2:

输入:p = [1,2], q = [1,null,2]
输出:false

示例 3:

输入:p = [1,2,1], q = [1,1,2]
输出:false

提示:

  • 两棵树上的节点数目都在范围 [0, 100] 内
  • -104 <= Node.val <= 104

分析:

两棵树上的节点数目都在范围 [0, 100] 内,说明这里需要进行空树的判断,我们需要先进行判断,当两棵树节点都为空时,满足条件,返回true.当两个节点只有一个为空时,不满足条件,返回false.当两个节点都不为空时,比较两个节点的val,不相等返回false.然后递归遍历两棵树.

代码展示:

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);
}
  1. 首先检查 p 和 q 是否同时为 NULL,如果是,说明两棵树都为空,返回 true
  2. 然后检查 p 和 q 是否其中一个为 NULL,另一个不是。如果是,说明两棵树中的节点个数不同,返回 false
  3. 接着检查 p 和 q 的当前节点值是否相同,如果不同,返回 false
  4. 最后,通过递归调用 isSameTree 函数比较两棵树的左子树和右子树是否相同,如果均相同则返回 true,否则返回 false

这段代码非常简洁而且有效地比较了两棵树是否相同。它利用了递归的特性,逐层比较树的节点值以及对应的子树,从而判断两棵树是否相同。

3. 对称二叉树

OJ链接--101. 对称二叉树 - 力扣(LeetCode)

题目描述:

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

  • 树中节点数目在范围 [1, 1000] 内
  • -100 <= Node.val <= 100

代码展示:

bool check(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 check(p->left, q->right) && check(p->right, q->left);
    else
        return false;
}

bool isSymmetric(struct TreeNode* root) {
    return check(root, root);
}

分析: 

  • check 函数:

    • 首先检查 p 和 q 是否同时为 NULL,如果是,说明两个节点都为空,返回 true
    • 然后检查 p 和 q 是否其中一个为 NULL,另一个不是。如果是,说明两个节点中的一个为空,另一个不为空,返回 false
    • 接着检查 p 和 q 的值是否相等,如果相等,则递归地比较 p 的左子树和 q 的右子树,以及 p 的右子树和 q 的左子树是否对称。如果均对称,则返回 true,否则返回 false
  • isSymmetric 函数:

    • 调用 check 函数,并传入根节点的左右子树作为参数进行比较。

这段代码利用递归的方式判断了一棵二叉树是否对称。它通过比较树的左右子树是否镜像对称来判断整棵树是否对称。整体上,这段代码实现了一个简洁而有效的对称性判断算法。

函数递归展开图:

4. 二叉树的前序遍历

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

题目描述:

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

输入:root = [1,2]
输出:[1,2]

示例 5:

输入:root = [1,null,2]
输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

分析:

注意这道题目的要求:

/**

 * 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* preorderTraversal(struct TreeNode* root, int* returnSize) {}

这里我们需要返回的是前序遍历好的数组,而且需要我们自己malloc创建.函数的参数一个是根节点一个是节点个数,要我们实现的是一个输出型函数.

代码展示: 

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

这段代码是关于二叉树前序遍历的实现,其中包括了计算二叉树节点总数和返回前序遍历结果的功能。让我们对这段代码进行详细分析:

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

这部分代码是计算二叉树节点总数的函数 TreeSize,使用了递归的方式来统计节点数量。在每个节点上,它会递归地调用自身来计算左子树和右子树的节点数,并加上当前节点。

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

这部分代码是实现前序遍历的函数 preOrder,它将遍历得到的节点值存储在数组 a 中,使用指针 pi 来跟踪数组的索引位置。

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

最后,preorderTraversal 函数整合了前两部分的功能。它先计算二叉树的节点总数,然后动态分配一个数组 a 来存储前序遍历的结果,并调用 preOrder 函数来填充数组。最终将数组返回。

总结

这段代码实现了计算二叉树节点总数并返回其前序遍历结果的功能。通过递归的方式遍历二叉树,将节点值按前序遍历顺序存储在数组中,并返回该数组。这样的实现使得我们能够方便地获取二叉树的前序遍历结果,并且能够有效地处理动态分配内存以存储结果。

5. 二叉树中序遍历

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

题目描述:

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

提示:

  • 树中节点数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

后面的中序和后序跟前序相差不大,这里就不过多解释. 

代码展示:

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

void inOrder(struct TreeNode* root, int* a, int* pi)
{
    if(root == NULL)
    {
        return;
    }
    inOrder(root->left,a,pi);
    a[(*pi)++] = root->val;
    inOrder(root->right,a,pi);
}

int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* a = (int*)malloc(sizeof(int)*(*returnSize));
    int i = 0;
    inOrder(root,a,&i);
    return a;
}

6. 二叉树的后序遍历

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

题目描述:

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

示例 1:

输入:root = [1,null,2,3]
输出:[3,2,1]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

提示:

  • 树中节点的数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

代码展示:

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

void postOrder(struct TreeNode* root, int* a, int* pi)
{
    if(root == NULL)
        return;
    
    postOrder(root->left, a, pi);
    postOrder(root->right, a, pi);
    a[(*pi)++] = root->val;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* a = (int*)malloc(sizeof(int)*(*returnSize));
    int i = 0;
    postOrder(root, a, &i);
    return a;
}

7. 另一颗树的子树

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

题目描述:

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true

示例 2:

输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false

提示:

  • root 树上的节点数量范围是 [1, 2000]
  • subRoot 树上的节点数量范围是 [1, 1000]
  • -104 <= root.val <= 104
  • -104 <= subRoot.val <= 104

分析:

遍历root树,找到root的所有子树与subroot树进行比较

代码展示:

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

注意:这里需要用到我们之前已经解决的相同的树的问题

该函数用来判断二叉树 subRoot 是否是二叉树 root 的子树。

逻辑解释

  1. 空树检查

    if(root == NULL) return false;

    如果 root 是空树,直接返回 false,因为空树不可能包含任何非空子树。

  2. 根节点值比较及完整树比较

    if(root->val == subRoot->val && isSameTree(root, subRoot)) return true;

    如果 root 的当前节点值与 subRoot 的根节点值相同,并且 root 从当前节点开始的子树与 subRoot 完全相同(通过调用 isSameTree 函数进行判断),则返回 true

  3. 递归检查左右子树

    return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);

    如果上述条件都不满足,则递归地检查 root 的左子树和右子树,看其中是否存在与 subRoot 相同的子树。如果任意一个子树包含 subRoot,返回 true

总结

这两个函数结合起来可以有效地判断一棵二叉树是否是另一棵二叉树的子树。具体步骤如下:

  1. 使用 isSubtree 函数遍历 root 的每个节点。
  2. 对于每个节点,检查该节点开始的子树是否与 subRoot 完全相同。
  3. 如果找到一个与 subRoot 相同的子树,立即返回 true
  4. 如果没有找到,继续遍历 root 的左右子树,直到所有节点都检查完毕。

这种方法通过递归遍历和子树比较,确保准确判断子树关系。

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

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

相关文章

三、MyBatis实践:提高持久层数据处理效率

三、MyBatis实践&#xff1a;提高持久层数据处理效率 目录 一、Mybatis简介 1.1 简介1.2 持久层框架对比1.3 快速入门&#xff08;基于Mybatis3方式&#xff09; 二、MyBatis基本使用 2.1 向SQL语句传参 2.1.1 mybatis日志输出配置2.1.2 #{}形式2.1.3 ${}形式 2.2 数据输入 2…

cesium 包络线

cesium 包络线 以下为源码直接复制可用 1、实现思路 通过turf.js中union方法来计算包络线官方地址:https://turfjs.fenxianglu.cn/ 闪烁线请查看cesium轨迹线(闪烁轨迹线) 2、示例代码 <!DOCTYPE html> <html lang="en"&g

SpringBoot配置第三方专业缓存技术Memcached 下载 安装 整合测试 2024年5000字详解

Memcached下载和安装 是一个国内使用量还是比较大的技术 打开文件夹 我们需要在命令行窗口启动 注意要以管理员方式运行 先尝试进入指定文件 然后又再次运行 下载 memcached.exe -d install 启动 memcached.exe -d start 停止 memcached.exe -d stop memcached.exe -d i…

Mysql数据库约束的概述 , 逐渐约束 , 主键自增 , 表关系的概念和外键 ,多表关系约束介绍和使用

约束和表设计 1、DQL查询语句-limit语句(掌握) 目标 能够掌握limit语句的使用 讲解 作用&#xff1a; LIMIT是限制的意思&#xff0c;所以LIMIT的作用就是限制查询记录的条数。 LIMIT语句格式: select * from 表名 limit offset, row_count; mysql中limit的用法&#…

轻松恢复丢失数据EasyRecovery你的数据守护神

数据丢失&#xff1f;别怕&#xff01;EasyRecovery来帮忙 大家好呀&#xff0c;今天我要分享一个我超级喜欢的数据恢复软件——EasyRecovery&#xff01;&#x1f389; 如果你也经历过误删文件、硬盘格式化或是意外丢失重要数据的尴尬和焦虑&#xff0c;那你一定要看看这个神器…

Bytebase 对接本地部署的 llama3 开启ChatSQL功能

Bytebase 是为开发人员、测试、DBA和运维工程师构建的数据库 DevOps 领域的&#xff0c;类 GitLab/GitHub 平台。 这篇文章主要关注 Bytebase SQL 编辑器中的 AI 增强功能。使用此功能您可以使用自然语言在 Bytebase SQL 编辑器中查询数据库。同时还能给出针对查询的索引建议&…

千呼新零售2.0【更新日志】持续更新ing

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看下…

【CSS in Depth 2 精译】1.5 渐进式增强

文章目录 1.5 渐进式增强1.5.1 利用层叠规则实现渐进式增强1.5.2 渐进式增强的选择器1.5.3 利用 supports() 实现特性查询启用浏览器实验特性 1.5 渐进式增强 要用好 CSS 这样一门不断发展演进中的语言&#xff0c;其中一个重要的因素就是要与时俱进&#xff0c;及时了解哪些功…

HTML静态网页成品作业(HTML+CSS)——动漫猪猪侠网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

测试辅助工具(抓包工具)的使用4 之 断点

抓包作用3&#xff08;绕过界面限制测试&#xff09; 1.为什么要绕过界面限制做测试&#xff1f; 原因&#xff1a;界面限制导致部分异常数据无法输入 2.如何绕过界面限制做测试&#xff1f; 绕过界面限制直接测试服务器 步骤&#xff1a; 1.设置断点 2.修改请求 3.修改响应…

微型操作系统内核源码详解系列五(五):cm3下Pendsv切换任务上篇

系列一&#xff1a;微型操作系统内核源码详解系列一&#xff1a;rtos内核源码概论篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列二&#xff1a;微型操作系统内核源码详解系列二&#xff1a;数据结构和对象篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列…

React的路由(ReactRouter)-路由导航跳转

1.第一步 // createBrowserRouter路由 RouterProvider组件 import {createBrowserRouter,RouterProvider} from react-router-dom // 创建router实例对象&#xff0c;并配置路由对应关系 const routercreateBrowserRouter([{path:/login,element:<div>我是登录页</di…

grafana 通过自定义API获取数据

一、安装插件 安装infinity插件 二、配置数据源 三、配置图表 1、数据 这边提供一个go的demo package mainimport ("math/rand""net/http""time""github.com/gin-gonic/gin" )func main() {router : gin.Default()rand.Seed(time.…

浅谈红队攻防之道-office文件免杀

最完美的状态&#xff0c;不是你从不失误&#xff0c;而是你从没放弃成长。 ∙菜单栏&#xff1a;集成了Cobalt Strike的所有功能。 ∙快捷功能区&#xff1a;列出了常用功能。 ∙目标列表区&#xff1a;根据不同的显示模式&#xff0c;显示已获取权限的主机及目标主机。 ∙…

“序列优化探究:最长上升子序列的算法发现与应用“

最长上升子序列 最长上升子序列是指在一个给定序列中&#xff0c;找到一个最长的子序列&#xff0c;使得子序列中的元素单调递增。例如&#xff0c;序列 [1, 3, 5, 4, 7] 的最长上升子序列是 [1, 3, 5, 7]&#xff0c;长度为4。 这是一个经典的动态规划问题。 假设dp[i]表示…

day2-web安全漏洞攻防-基础-弱口令、HTML注入(米斯特web渗透测试)

day2-web安全漏洞攻防-基础-弱口令、HTML注入&#xff08;米斯特web渗透测试&#xff09; 1&#xff0c;漏洞2&#xff0c;弱口令3&#xff0c;爆破&#xff08;1&#xff09;Burpsuite&#xff08;2&#xff09;攻击类型 4&#xff0c;HTML针剂注入 1&#xff0c;漏洞 挖掘和利…

Adobe Photoshop图像处理软件下载安装,ps 2024版本安装包分享

Adobe Photoshop&#xff0c;这款业界闻名的图像处理软件&#xff0c;简称“PS”&#xff0c;以其强大的功能和广泛的应用领域&#xff0c;赢得了无数设计师、摄影师和创意工作者的青睐。 在Photoshop的世界里&#xff0c;用户能够随心所欲地对图像进行裁剪、缩放、调整色彩和…

Git 中 pull 操作和 rebase 操作的不同

由于在开发过程中&#xff0c;pull 操作和 rebase 操作都是用来合并分支的&#xff0c;所以我就常常分不清这两个操作具体有什么区别&#xff0c;所以才有了这篇博客来做个简单区分&#xff0c;具体细致差别还请移步到官方文档&#xff1a;Git - Reference (git-scm.com) 1&am…

怎么优化ArcEngine组件开发mfc程序界面?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

接口测试工作准备

前面已经讲了接口测试的原理&#xff0c;接下来讲接口测试如何准备。分为了解项目背景、收集项目相关资料、部署接口测试环境。明确接口测试相关成果物。接口测试采用的技术/工具。接口测试工作分工等。 1、了解项目背景 1、首先我们应该去了解项目的应用范围&#xff0c;了解…