Java-数据结构-二叉树习题(1)

news2025/1/19 13:42:04

对于二叉树的学习,主要的还是得多多练习~毕竟二叉树属于新的知识,并且也并不是线性结构,再加上经常使用递归的方法解决二叉树的问题所以代码的具体流程还是无法看到的,只能通过画图+想象,所以还是必须多加练习,使自己见识的题型更多,才会熟能生巧~

第一题、相同的树

📚 思路提示

这一题还是很好想的~,对于比较两个相同的树,我们只需要像之前模拟实现二叉树的方法一样,将它拆解成 " 左子树和右子树的子问题 " 将两个树的左子树与左子树进行比较,右子树与右子树进行比较。

并且再通过递归的方式不断对子树再次执行 " 拆解成新的左子树和右子树 比较对应子树是否相同 "的操作。

直到从最底层的子树(叶子结点)递归回到两个数的根节点时,如果中途出现过 false 那么返回的就是 false ,反之则返回 true。

而其中结点的比较过程,需要判断的问题有这几种

📕 如果 p == null 并且 q == null 则代表同时达到叶子节点,返回 true 

📕 如果 p 和 q 没有进入上述判断语句,并且此时 p == null 或者 q == null 则代表有一方提前访问到了子树,则代表两棵子树的长度不相同,返回 false

📕 如果 p 和 q 都没进入上述两个判断语句,那么此时 p 和 q 都不为 null ,此时正常对它们的值进行比较,如果 p.val 不等于 q.val 则返回false

📕 最后递归函数,两次函数传参 " (p.left,q.left) && (p.right,q.right) ",只有全 true 的时候,才能最后返回 true 

图示

📖 代码示例

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null){
            return true;
        }
        if(p == null || q == null || p.val != q.val){
            return false;
        }
        return isSameTree(p.left,q.left) && isSameTree(p.right,q.right); 
    } 
}

第二题、另一棵树的子树

📚 思路提示

对于此题,其实就是上一题的引申~我们想要判断,一棵树是否是另一棵树的子树,那么我们首先要在另一棵树中找到这棵树的根节点

找到根节点后,我们就可以对这棵树进行判断啦~没错的,也就是判断它是否为相同的树,那么我们直接使用上一题的方法即可,首先找到根节点,然后再调用"判断是否为相同树"的方法进行递归即可

那么如何找到这个根结点呢?有以下的步骤

📕 不断递归该方法,分别传参判断(root.left)和(root.right),与subRoot的根节点是否相同(直接通过判断两棵树是否相等的方法即可)

📕 如果 root 等于 null 则返回 false 代表该子树中没找到那棵相同子树,退出递归

📕 如果"两树相同方法返回了true",则返回 true,代表该子树中找到了那棵相同子树,退出递归 

图示

📖 代码示例

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root == null){
            return false;
        }
        if(isSameTree(root,subRoot)){
            return true;
        }
        return isSubtree(root.left,subRoot) || isSubtree(root.right,subRoot);
    }
        public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null){
            return true;
        }
        if(p == null || q == null || p.val != q.val){
            return false;
        }
        return isSameTree(p.left,q.left) && isSameTree(p.right,q.right); 
    } 
}

第三题、翻转二叉树

想要翻转二叉树,就需要我们对树进行遍历,所以该题有多种解法,取决于它采用何种遍历。

(需要注意的是,不是将根节点下的左右子树翻转就好了,而是需要每棵树的子树都要进行翻转)

📚 思路提示[前/中/后序遍历]

还是很简单的思路,只需要不断的递归函数,再将其中的结点互换即可~这里就不再过多赘述啦~我们直接看图片

图示[前/中/后序遍历](1)

📖 代码示例(1)

    public TreeNode invertTree(TreeNode root){
        if(root == null){
            return null;
        }
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

上述方法是通过借助中间变量 tmp 来实现的,接下来我们不用 tmp,而是将左右子树两个结点存储,再递归回去分别进行交换。

图示[前/中/后序遍历](2)

📖 代码示例(2)

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);
        root.left = right;
        root.right = left;
        return root;
    }
}

📚 思路提示[层序遍历]

层序遍历,在上一篇博客中我们已经讲解到了~就是将每一层的结点按照从左至右,从上至下的顺序存储到队列中,并且通过循环,将队列中的元素(一层的结点)依次出队列打印出来。

而在这题中,我们只需要在结点出队列的时候,同时将每个结点的左右子结点进行互换即可~思路还是很简单的,我们直接看图看代码~~

图示[层序遍历]

📖 代码示例(3)

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        Deque<TreeNode> deque = new LinkedList<>();
        deque.push(root);
        while (!deque.isEmpty()) {
            TreeNode node = deque.removeLast();
            if (node.left != null) {
                deque.push(node.left);
            }
            if (node.right != null) {
                deque.push(node.right);
            }
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        return root;
    }
}

第四题、反转二叉树的奇数层

我们之所以先讲了一个翻转二叉树,其实目的就是引出这道题~至于这道题应该怎么求解,其实也是换汤不换药,在这里我们提供两种解法:

📕 层序排列

📚 思路提示(1)

经过上面的那道题,相信大家对层序遍历也有了比较深刻的理解了~而对于这道题也并不难,上面的代码要求我们将所有层的左右结点都进行翻转,这题则是将奇数层的左右结点进行翻转。

所以我们只需要在循环外部定义一个变量,通过这个变量来记录每次遍历时队列中计入结点对应为第几层,再将奇数层的结点相互调换即可~

使用该方法解题有以下几点需要注意

📕 注意题中的根节点层数是以0开始的,而并非是1

📕 外层while循环不能再像上一题一样,每出队一个结点(及子树),就判断一次while循环,这样就无法获取层数

📕 而想修改上述问题,我们就需要在while循环内部再设置一个循环我们定义此时的队列元素个数为len,定义条件while(len-- > 0),并且将入队列和出队列的操作都在这个循环内部进行,这样就能解决查找层数不准确的问题了。

📕 同时,如果我们想做到上述,在一次while大循环中解决多次结点入队列出队列,那么对应的,我们也要同时应对交换结点这一步,我这里采用的方法是"当层数为奇数时,将队列中结点存入一个顺序表中,通过双指针的方法,将其中结点值进行翻转"~注意,这里不推荐使用链表,因为速度的消耗极大:

顺序表:

链表:

图示(1)

📖 代码示例(1)

class Solution {
    public TreeNode reverseOddLevels(TreeNode root) {
        if (root == null) {
            return null;
        }
        int num = 0;
        Deque<TreeNode> deque = new LinkedList<>();
        deque.push(root);
        while (!deque.isEmpty()) {
            int len = deque.size();
            if (num % 2 != 0) {
                List<TreeNode> list = new ArrayList<>(deque);
                for (int i = 0, j = list.size() - 1; i < j; i++, j--) {
                    TreeNode tmpl = list.get(i);
                    TreeNode tmpr = list.get(j);
                    int tmp = tmpl.val;
                    tmpl.val = tmpr.val;
                    tmpr.val = tmp;
                }
            }
            while(len-- > 0){
                TreeNode node = deque.removeLast();
                if (node.left != null) {
                    deque.push(node.left);
                }
                if (node.right != null) {
                    deque.push(node.right);
                }
            }
            num++;
        }
        return root;
    }
}

📕 分解子树法

📚 思路提示(2)

这种解法相对于上一种解法就要简单许多了,同时效率也达到了显著的提升~既不用队列,也不用顺序表,更用不上循环交换节点的值!~

果然树还是得用递归呀~

其实还是很好理解的,平时我们进行结点的交换,只是传入root的左结点和右结点,就能够通过递归将所有结点都互换

那么我们只需要创建一个方法,使它的参数再多一个int类型,每次递归都在它的原基础上+1,那么就可以很清晰的分辨出是奇数层还是偶数层

至于过程也就是和平时的翻转结点多了个判断层数的步骤,这里就不再过多的画图赘述啦~我们直接看代码,包看得懂的。

📖 代码示例(2)

class Solution {
    public TreeNode reverseOddLevels(TreeNode root) {
        if (root == null) {
            return null;
        }
        move(root.left, root.right, 1);
        return root;
    }
    public void move(TreeNode left, TreeNode right, int num) {
        if(left == null || right == null){
            return;
        }
        if (num % 2 != 0) {
            int tmp = left.val;
            left.val = right.val;
            right.val = tmp;
        }
        move(left.left, right.right, num + 1);
        move(left.right, right.left, num + 1);
    }
}

那么这次关于二叉树的习题相关知识,就为大家分享到这里啦,作者能力有限,如果有讲得不清晰或者不正确的地方,还请大家在评论区多多指出,我也会虚心学习的!那我们下次再见哦

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

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

相关文章

(二)afsim第三方库编译(qt编译)

注意&#xff1a;源码编译的路径不能有中文否则报错&#xff0c;压缩包必须用官网下载的xz格式解压的才可以&#xff0c;否则sudo ./configure命令找不到 先编译openssl3.1.1软件包&#xff0c;否则编译的qt库将不支持network&#xff0c;相关库的编译(上文&#xff08;一&…

【QT用户登录与界面跳转】

【QT用户登录与界面跳转】 1.前言2. 项目设置3.设计登录界面3.1 login.pro参数3.2 界面设置3.2.1 登录界面3.2.2 串口主界面 4. 实现登录逻辑5.串口界面6.测试功能7.总结 1.前言 在Qt应用程序开发中&#xff0c;实现用户登录及界面跳转功能是构建交互式应用的重要步骤之一。下…

【docker踩坑记录】

docker踩坑记录 踩坑记录(持续更新中.......)docker images 权限问题 踩坑记录(持续更新中…) docker images 权限问题 permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.s…

搜维尔科技:Xsens人形机器人解决方案的优势

Xsens 致力于推动人形机器人技术的发展&#xff0c;塑造机器人与人类环境无缝融合的未来&#xff0c;通过创新精确和协作&#xff0c;协助生产和服务&#xff0c;改善人类生活和产业。 Xsens通过人形跟随捕捉详细的人体运动数据&#xff0c;使机器人能够学习类人的动作&#x…

国内微电子(集成电路)领域重点高校的特色与优势

本文旨在梳理国内微电子&#xff08;集成电路&#xff09;领域重点高校的特色与优势&#xff0c;为有志于从事相关领域的学生提供参考。文章将从学科特色、科研实力&#xff08;以ISSCC论文为参考之一&#xff09;、行业认可度等方面进行分析&#xff0c;并强调实验室、导师、研…

leetcode707-设计链表

leetcode 707 思路 本题也是用了虚拟头节点来进行解答&#xff0c;这样的好处是&#xff0c;不管是头节点还是中间的节点都可以当成是中间节点来处理&#xff0c;用同一套方法就可以进行处理&#xff0c;而不用考虑太多的边界条件。 下面题目中最主要的实现就是添加操作addA…

数据结构-栈队列OJ题

文章目录 一、有效的括号二、用队列实现栈三、用栈实现队列四、设计循环队列 一、有效的括号 (链接&#xff1a;ValidParentheses) 这道题用栈这种数据结构解决最好&#xff0c;因为栈有后进先出的性质。简单分析一下这道题&#xff1a;所给字符串不是空的也就是一定至少存在一…

MindAgent:基于大型语言模型的多智能体协作基础设施

2023-09-18 &#xff0c;加州大学洛杉矶分校&#xff08;UCLA&#xff09;、微软研究院、斯坦福大学等机构共同创建的新型基础设施&#xff0c;目的在评估大型语言模型在游戏互动中的规划和协调能力。MindAgent通过CuisineWorld这一新的游戏场景和相关基准&#xff0c;调度多智…

近红外简单ROI分析matlab(NIRS_SPM)

本次笔记主要想验证上篇近红外分析是否正确&#xff0c;因为叠加平均有不同的计算方法&#xff0c;一种是直接将每个通道的5分钟实时长单独进行叠加平均&#xff0c;另一种是将通道划分为1分钟的片段&#xff0c;将感兴趣的通道数据进行对应叠加平均&#xff0c;得到一个总平均…

开发神器之cursor

文章目录 cursor简介主要特点 下载cursor页面的简单介绍切换大模型指定ai学习的文件指定特定的代码喂给ai创建项目框架文件 cursor简介 Cursor 是一款专为开发者设计的智能代码编辑器&#xff0c;集成了先进的 AI 技术&#xff0c;旨在提升编程效率。以下是其主要特点和功能&a…

电脑风扇声音大怎么办? 原因及解决方法

电脑风扇是电脑的重要组件之一&#xff0c;它的作用是为电脑的各个部件提供冷却&#xff0c;防止电脑过热。然而&#xff0c;有时候我们会发现电脑风扇的声音特别大&#xff0c;不仅影响我们的使用体验&#xff0c;也可能是电脑出现了一些问题。那么&#xff0c;电脑风扇声音大…

SpringBoot错误码国际化

先看测试效果&#xff1a; 1. 设置中文 2.设置英文 文件结构 1.中文和英文的错误消息配置 package com.ldj.mybatisflex.common;import lombok.Getter;/*** User: ldj* Date: 2025/1/12* Time: 17:50* Description: 异常消息枚举*/ Getter public enum ExceptionEnum {//…

软考高级5个资格、中级常考4个资格简介及难易程度排序

一、软考高级5个资格 01、网络规划设计师 资格简介&#xff1a;网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力&#xff1b;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介&#xff1a;系统分…

如何通过 Apache Airflow 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 了解如何通过 Apache Airflow 将数据导入 Elasticsearch。 Apache Airflow Apache Airflow 是一个旨在创建、安排&#xff08;schedule&#xff09;和监控工作流的平台。它用于编排 ETL&#xff08;Extract-Transform-Load&#xff0…

STM32 学习笔记【补充】(十)硬件I2C读写MPU6050

该系列为笔者在学习STM32过程&#xff08;主线是江科大的视频&#xff09;中的记录与发散思考。 初学难免有所纰漏、错误&#xff0c;还望大家不吝指正&#xff0c;感谢~ 一、I2C 外设简介 I2C&#xff08;Inter-Integrated Circuit&#xff09;是一种多主多从的串行通信协议…

QT信号槽 笔记

信号与槽就是QT中处理计算机外设响应的一种机制 比如敲击键盘、点击鼠标 // 举例&#xff1a; 代码&#xff1a; connect(ls,SIGNAL(sig_chifanla()),ww,SLOT(slot_quchifan())); connect(ls,SIGNAL(sig_chifanla()),zl,SLOT(slot_quchifan()));connect函数&#xff1a;这是…

【React】插槽渲染机制

目录 通过 children 属性结合条件渲染通过 children 和 slot 属性实现具名插槽通过 props 实现具名插槽 在 React 中&#xff0c;并没有直接类似于 Vue 中的“插槽”机制&#xff08;slot&#xff09;。但是&#xff0c;React 可以通过 props和 children 来实现类似插槽的功能…

openharmony电源管理子系统

电源管理子系统 简介目录使用说明相关仓 简介 电源管理子系统提供如下功能&#xff1a; 重启服务&#xff1a;系统重启和下电。系统电源管理服务&#xff1a;系统电源状态管理和休眠运行锁管理。显示相关的能耗调节&#xff1a;包括根据环境光调节背光亮度&#xff0c;和根…

数据库(中)11讲

用颜色、有否下划线对应&#xff01; E-R图

图像去雾数据集的下载和预处理操作

前言 目前&#xff0c;因为要做对比实验&#xff0c;收集了一下去雾数据集&#xff0c;并且建立了一个数据集的预处理工程。 这是以前我写的一个小仓库&#xff0c;我决定还是把它用起来&#xff0c;下面将展示下载的路径和数据处理的方法。 下面的代码均可以在此找到。Auo…