二叉树进阶oj练习

news2024/9/29 23:22:49

 


目录

1. 根据二叉树创建字符串

1)思路

 2)代码实现

2. 二叉树前序非递归遍历实现

1)思路:

2)代码实现 

3. 二叉树中序非递归遍历实现

1)思路:

2)代码实现:

4. 二叉树后序非递归遍历实现

 1)思路:

 2)代码实现:



1. 根据二叉树创建字符串

题目:

    给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。

    空节点使用一对空括号对 "()" 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

 我们先看看实例一:

根据题目可知, 最终的输出是采用前序遍历的方式,前序遍历我们都懂,也就是“根左右”嘛,那么是如何得到这个输出的呢?

1)思路

其实很简单:我们观察示例给出的输出,首先我们从根节点1出发,根节点为空吗?不为空时,会把这个值输出,前序遍历的话,根节点走完是不是就往左边走了,根节点的左边(也就是2)是不是空?不是则打印左括号“(”,此时我们已经遍历到了2的位置,此时对于它来说可以看成是一颗根为2的新树,重复上述加粗的过程。我们是不是就得到输出的“1(2(4”了?

 当节点走到4的时候,根节点为4,其左边为空,右边也为空,说明4这颗树已经完了,这个时候就直接加个“)”,注意:什么时候加 “)” ?树走完了的时候才加。当树走完了就回去,此时4就回到了2,如下图所示:

从示例的解释中我们可以知道,2的右侧为空时应该加一个“()”,但是实际输出中省略了,也就是说当左边不为空,右边为空时,直接返回(也就是这棵树已经走完) ,前面说了,树走完要干嘛?加  ")"  !所以就回到了1,此时对应输出就是:1(2(4))  这两个右括号分别是4和2这两棵树完时添加的,回到1时进入到右边的3,如下图所示:

1的右边不为空,所以添加“(”, 并把值添加进去,3的左右都为空,直接返回(树已经走完),加")"。就形成了示例中的输出 。

 

我们看实例二,这里有点区别的就是2的左树为空,右树不为空时不会省略"()"。

 2)代码实现

public String tree2str(TreeNode root) {
        StringBuilder sbu = new StringBuilder();
        tree2strChild(root,sbu);
        return sbu.toString();
}

public void tree2strChild(TreeNode root,StringBuilder sbu) {
        if(root == null) {
            return;
        }
        sbu.append(root.val);

        //1、先递归左树
        if(root.left != null) {
            sbu.append("(");
            tree2strChild(root.left,sbu);
            sbu.append(")");
        }else {
            //root == 4
            if(root.right == null) {
                return;
            }else {
                sbu.append("()");
            }
        }
        //2、递归右树
        if(root.right != null) {
            sbu.append("(");
            tree2strChild(root.right,sbu);
            sbu.append(")");
        }else {
            return;
        }
}

2. 二叉树前序非递归遍历实现

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

1)思路:

前序遍历,“根左右”。如果是递归做法的话,这题对大家都没有难度,我们沿着递归的路径走去完成这题。根节点A打印很容易,但是打印了我们能不能丢了这个信息?不能,因为A的右边这棵树我们还要记录下来,所以打印A归打印A,A本身要记录下来,那么我们要怎么做呢?

我们先申请一个栈,从根节点A出发,root这个根节点不动,定义一个cur来代替根节点走,每次当节点不为空,我们就打印下来,同时把节点存进栈中 ,然后让cur沿着左边走。当我们把左边走完,cur就到了空的位置,说明“根左”走完了,那么我们怎么走“右”?如下图所示:

我们看栈顶元素,弹出栈顶元素 D,让cur等于栈顶元素的右边,为空时就继续弹出栈顶元素(B),然后继续让cur等于栈顶元素的右边(E),重复刚开始操作(节点不为空就打印并存入栈中),这样便可以完成这道题了。如下图所示:

我们可以定义一个top来存栈顶元素,如上述操作中,栈顶元素先是D,然后变成了E。

2)代码实现 

public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                list.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return list;
}

3. 二叉树中序非递归遍历实现

1)思路:

这题是中序遍历,“左根右”,所以我们要先打印左树再打印根,其实上面的前序遍历你弄懂了,这题中序遍历也不是什么大问题了,只是在前序遍历的基础上把打印的地方修改一下即可,在上题前序遍历中,我们只要一入栈就把该节点的值打印出来了,而这题我们只需要把打印的位置改到出栈的时候即可,为什么是出栈时候呢?看下图:

当cur走到D的左树时候为空,那么这个时候就要出栈,上文说的,出栈就要打印,这个时候就会把D打印出来(定义top来记录这个栈顶元素),同时cur 变成 栈顶元素的右边,如下图:

此时cur还是为空,所以继续出栈,栈顶元素就变为B,这个时候就会打印B,同时cur更新为B的右侧,如下图:

这里博主就不再赘述了,相信各位小伙伴已经清楚了,我们看代码:

2)代码实现:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            list.add(top.val);
            cur = top.right;
        }
        return list;
    }
}

4. 二叉树后序非递归遍历实现

 1)思路:

后续遍历,“左右根”,跟上面一样,只要根不为空就放入栈中,但是不能打印! 如下图:

 

当cur走到空的时候能否出栈弹出D?不能,因为有一种可能就是D的右侧还有数据,所以这里有两种可能,一种D的右树为空,一种D的右树不为空,如下图所示D的右侧不为空: 

这种情况我们只能将D拿来指示一下,更新cur到D的右侧,但是不出栈D,所以我们要把pop改成peek,后序遍历的代码跟我们上面的不太一样,如上图。所以我们根据中序遍历的代码修改如下:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
        }
        return list;
    }
}

所以我们接下来的代码就要用if-else语言,写D的右树为空和D的右树不为空时的情况,if D的右树为空,那么就可以直接弹出栈顶元素同时打印,如下图:

else D的右树不为空,老规矩压栈,然后cur往H的左边走为空,如下图所示:

接着cur往H的右边走也为空,我们再次进入循环,这是个关键点,代码会再次peek一次,如下图,这一部分的代码如下:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }

            TreeNode top = stack.peek();
            if(top.right == null) {
                stack.pop();
                list.add(top.val);
            }else {
                cur = top.right;
            }
        }
        return list;
    }
}

 

  peek完成后,D的右边为空吗?不为空则走else,cur = cur.right; 这个时候cur = H,又把H放进去了栈,这个时候代码就会出现死循环,为什么会死循环?因为只考虑了右边为空,而不为空会先被打印,也就说什么时候打印有两种情况,右边为空和右边已经被打印了,所以要记录下来上一个被打印的节点,所以我们接下来的问题就是如何证明被打印过的呢?我们回到D的左边,如下图:

 依据代码,top的右边为空吗?D不为空cur = cur.right. 如下:

 再次进入循环,把H压栈,同时cur再往左边走:

 

接下来代码再peek一下,top更新为H,H的右边为空,所以出栈并打印H,

 

这个时候定义一个引用prev来指向当前的top来证明被打印, 此时if走完了,此时cur为空,如下图:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null  || 被打印过) {
                stack.pop();
                list.add(top.val);
                prev = top;
            }else {
                cur = top.right;
            }
        }
        return list;
    }
}

 然后代码继续向后走,D的右侧为空吗?不为空,但是被打印过吗?也就是说top.right == prev吗?满足条件,此时就会弹出D,同时打印,prev更新为D,如下图:

依此类推,通过prev便可以记录下来最近一次被打印过的节点。 

 2)代码实现:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null  || top.right == prev) {
                stack.pop();
                list.add(top.val);
                prev = top;
            }else {
                cur = top.right;
            }
        }
        return list;
    }
}

感谢阅读,希望对大家有所帮助!! 

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

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

相关文章

Programming Abstractions in C阅读笔记:p303-p305

《Programming Abstractions in C》学习第74天&#xff0c;p303-p305总结&#xff0c;总计3页。 一、技术总结 1.时间复杂度分类(complexity classes) ClassNotationExampleconstantO(1)Returning the first element in an arraylogarithmicO(logN)Binary search in a sorte…

三菱plc控制双控电磁阀(从接线到程序)

目录 硬件设备 电磁阀的类型&#xff08;下图为例&#xff09; 三菱plc的类型&#xff08;西门子控正COOM接接正极&#xff0c;三菱控负COM接负极&#xff09; 气缸图 三菱plc与双控电磁阀接线 输出接线图&#xff08;COOM输出的公共端&#xff0c;三菱控负COM接负极&#x…

大学生课程|统计基础与python分析6|主流的评估方法:ROC曲线和KS曲线(免费下载所有课程材料)

目录 1 ROC曲线 2.查看混淆矩阵 3.实战评估股票客户流失预警模型 4.阈值的取值方法 5.KS曲线与KS值 6.获取KS值对应的阈值 7.获取KS值 对于二分类模型来说&#xff0c;主流的评估方法有ROC曲线和KS曲线两种方法 1 ROC曲线 之前已经获得了模型的准确度为79.77%&#xf…

【USENIX论文阅读】Day2

Birds of a Feather Flock Together: How Set Bias Helps to Deanonymize You via Revealed Intersection Sizes&#xff08;"物以类聚&#xff1a;集合偏差如何帮助去匿名化——通过揭示交集大小&#xff09; Xiaojie Guo, Ye Han, Zheli Liu, Ding Wang, Yan Jia, Jin L…

高级光线传播与高级外观建模

一.高级光线传播 1、无偏的 ①有偏VS无偏 蒙特卡洛估计出的结果的期望永远是对的 eg&#xff1a;PT中不管有多少样本&#xff0c;期望都是定积分的值 有偏的&#xff1a;估计出的结果的期望和积分的值不一样 一个特殊情况&#xff08;一致的&#xff09;&#xff1a;极限定…

【web APIs】1、(学习笔记)有案例!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、概念二、使用步骤1.获取DOM对象2.操作元素内容3.属性修改3.1.常用属性修改3.2.控制样式属性3.3.操作类名(className) 操作CSS3.4.操作表单元素属性3.5.自定…

Mysql常见函数和用法(重点)

where子句中经常使用的运算符 -- 查询总分大于200分的所有同学 select * from student2 where (chineseenglishmath)>200; -- 查询math大于60 并且(and)id大于4的学生成绩 select * from student2 where math>60 and id>4; -- 查询英语成绩大于语文成绩的同学 select …

面试总结之JVM入门

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f380;你为什么要学习JVM&#xff1f;&#x1f380;JVM的作用 &#x1f380;JVM的构成&#xff08;5大类&#xff09;&#x1f3e8;1.类加载系统&#x1f415;类什么时候会被加…

《Docker 简易速速上手小册》第2章 容器和镜像(2024 最新版)

文章目录 2.1 理解 Docker 容器2.1.1 重点基础知识2.1.2 重点案例&#xff1a;使用 Docker 运行 Python 应用2.1.3 拓展案例 1&#xff1a;Docker 中的 Flask 应用2.1.4 拓展案例 2&#xff1a;Docker 容器中的数据分析 2.2 创建与管理 Docker 镜像2.2.1 重点基础知识2.2.2 重点…

k8s(5)

目录 使用Kubeadm安装k8s集群&#xff1a; 初始化操作&#xff1a; 每台主从节点&#xff1a; 升级内核&#xff1a; 所有节点安装docker &#xff1a; 所有节点安装kubeadm&#xff0c;kubelet和kubectl&#xff1a; 修改了 kubeadm-config.yaml&#xff0c;将其传输给…

网络攻防之网络扫描

目录 1、进行ping扫描 2、进行TCP SYN扫描 3、进行TCP全连接扫描 4、进行FIN扫描 5、进行UDP扫描 6、进行操作系统扫描 7、进行主机全面扫描 8、对网络号进行扫描 环境配置拓扑图&#xff1a; 实验前准备 查看kali和靶机的ip地址信息&#xff1a; 查看两台主机是否能互…

vue中循环多个li(表格)并获取对应的ref

有种场景是这样的 <ul><li v-for"(item,index) in data" :key"index" ref"???">{{item}}</li> </ul> //key值在项目中别直接用index&#xff0c;最好用id或其它关键值const data [1,2,3,4,5,6]我想要获取每一个循环并…

数据结构知识点总结-线性表(3)-双向链表定义、循环单链表、、循环双向链表、静态链表、顺序表与链表的比较

双向链表定义 单链表结点中只有一个指向其后继的指针&#xff0c;这使得单链表只能从头结点依次顺序地向后遍历。若要访问某个结点的前驱结点&#xff08;插入、删除操作时&#xff09;&#xff0c;只能从头开始遍历&#xff0c;访问后继结点的时间复杂度为 O(1) &#xff0c; …

详解java类型转换

✨✨ 所属专栏&#xff1a; Java基石&#xff1a;深入探索Java核心基础✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 引言 在Java编程中&#xff0c;类型转换是将一个数据类型的值转换为另一个数据类型的过程。Java中的类型转换主…

Django学习笔记-ModelForm使用(完全依赖)

1.创建模型 ,code,name,sex,entrydate 2.模型映射 python manage.py makemigrations myapp01,python manage.py migrate 3.创建模型表单,继承forms.ModelForm,Meta:元数据,models需引入,fields填写引用的模型变量 4.创建testModelForm.html,添加urls 5.views编写testmodelfo…

前端sql条件拼接js工具

因为项目原因&#xff0c;需要前端写sql&#xff0c;所以弄了一套sql条件拼接的js工具 ​ /*常量 LT : " < ", LE : " < ", GT : " > ", GE : " > ", NE : " ! ", EQ : " ", LIKE : " like &qu…

字典树入门

//本题是一道字典树的模板题 //字典树是一种高效率存储多个字符串的数据结构 //其每个结点的权值代表以该结点结尾的字符串的数量,每条边存储一个字符 //从根结点开始,按某一路径遍历到某一结点,即得到一种字符串,其个数等于当前结点存储的数值 //如从根结点开始,依次走过abc三…

【iOS ARKit】ARWorldMap

ARWorldMap 用于存储 ARSession 检测扫描到的空间信息数据&#xff0c;包括地标&#xff08;Landmark&#xff09;、特征点&#xff08;Feature Point&#xff09;、平面&#xff08;Plane&#xff09;等&#xff0c;以及使用者的操作信息&#xff0c;如使用者添加的 ARAnchor …

【Vue】组件通信组件通信

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;JVM ⛺️稳中求进&#xff0c;晒太阳 组件通信 组件通信&#xff0c;就是指组件与组件之间的数据传递 组件的数据是独立的&#xff0c;无法直接访问其他组件的数据想用其他组件的数据--&…

【深度学习】Pytorch教程(十三):PyTorch数据结构:5、张量的梯度计算:变量(Variable)、自动微分、计算图及其可视化

文章目录 一、前言二、实验环境三、PyTorch数据结构1、Tensor&#xff08;张量&#xff09;1. 维度&#xff08;Dimensions&#xff09;2. 数据类型&#xff08;Data Types&#xff09;3. GPU加速&#xff08;GPU Acceleration&#xff09; 2、张量的数学运算1. 向量运算2. 矩阵…