java算法day11

  • 二叉树的递归遍历
  • 二叉树的非递归遍历写法
  • 层序遍历

递归怎么写?

按照三要素可以保证写出正确的递归算法:

1.确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

2.确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

3.确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。


前置必会

一定要会自己写出树的类定义

public class TreeNode{TreeNode left;TreeNode right;int val;TreeNode(){}TreeNode(int val){this.val = val}TreeNode(int val,TreeNode right,TreeNode left){this.val = val;this.left = left;this.right = right;
}

144.二叉树的前序遍历

递归写法

class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();dfs(root,result);return result;}void dfs(TreeNode root,List<Integer> result){if(root==null){return;}result.add(root.val);dfs(root.left,result);dfs(root.right,result);}}

非递归写法:
我认为就是记思路。
1.用一个栈作为辅助。
2.前序遍历是根左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。之所以是反过来是因为,这样出栈的时候才是根左右的顺序。直接来看图模拟
请添加图片描述
即每次在处理根节点的时候,将根节点出队,然后将其右孩子先进栈,再将左孩子进栈。
这样进行处理之后,出栈的结果就会是前序遍历的结果。

如果还是不懂,我建议直接结合代码,然后结合上面图,记下来这个做法。我觉得这个直接想是不好想的。

非递归其实就是用辅助数据结构,配合循环

class Solution {public List<Integer> preorderTraversal(TreeNode root) {//结果集List<Integer> result = new ArrayList<>();//特判if(root == null){return result;}//创建辅助栈Deque<TreeNode> st = new ArrayDeque<>();//根节点先入栈st.push(root);//当栈不空的时候while(!st.isEmpty()){//先处理根节点,即将根节点出栈。这就对应着根TreeNode node = st.pop();//将出栈的根节点加入结果集result.add(node.val);//先将右节点加入栈中,可以这么想,早点加入,那么就晚点出。所以右节点出的时候,要比左节点晚,那么这里出也就是和上面节点出栈一个道理。所以这里就完成了根左右里面的根左。因为左节点进的晚,出的就早。if(node.right!=null){st.push(node.right);}//然后才是到左节点,晚点进就可以早点出。if(node.left!=null){st.push(node.left);}}return result;}
}

这是我第二写总结的流程:
1.初始化
创建一个空的结果列表
创建一个辅助栈
将根节点压入栈中

2.主循环: 当栈不为空时,执行以下操作:

a. 处理当前节点:
从栈中弹出一个节点
将该节点的值添加到结果列表中

b. 压入子节点:
如果该节点有右子节点,将右子节点压入栈中
如果该节点有左子节点,将左子节点压入栈中
返回结果列表

这种方法的关键点在于
根节点最先被处理,这符合前序遍历的"根-左-右"顺序。
右子节点先于左子节点入栈,这确保了左子节点会先于右子节点被处理。
这种非递归方法的优点是:

避免了递归调用的开销
对于深度很大的树,可以防止栈溢出
实现简单,易于理解
与中序遍历的非递归方法相比,前序遍历的非递归实现更为直观,因为它可以直接按照遍历顺序处理节点,而不需要像中序遍历那样先到达最左节点。

145.二叉树的后序遍历

递归写法

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result  = new ArrayList<>();dfs(root,result);return result;}public void dfs(TreeNode root,List<Integer> result){if(root==null){return;}dfs(root.left,result);dfs(root.right,result);result.add(root.val);}
}

非递归写法:
这个解法是一个技巧解。
由于前序遍历是根左右,而后续遍历是左右根。所以如果调整一下前序遍历的顺序,先加左节点,再加右节点,那么得到的结果就是按根右左规则得到的。所以此时做翻转,那么得到的结果就是按左右根得到的结果。与后序遍历的结果不谋而合。
所以解法就是线序遍历调整左右入栈方式,然后再对最终结果做翻转。

class Solution {public List<Integer> postorderTraversal(TreeNode root) {Deque<TreeNode> st = new ArrayDeque<>();List<Integer> result = new ArrayList<>();if(root==null){return result;}st.push(root);while(!st.isEmpty()){TreeNode node = st.pop();result.add(node.val);if(node.left!=null){st.push(node.left);}if(node.right!=null){st.push(node.right);}}Collections.reverse(result);return result;}
}

94.二叉树的中序遍历

递归写法

class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();dfs(root,result);return result;}public void dfs(TreeNode root,List<Integer> result){if(root==null){return;}dfs(root.left,result);result.add(root.val);dfs(root.right,result);}
}

非递归写法:
看这个图,然后结合图片,记下这个做法:
请添加图片描述
1.初始化
创建一个空的结果列表和一个空的栈
将当前节点设置为根节点

2.主循环: 当当前节点不为空或栈不为空时,执行以下操作:

a. 左子树遍历
如果当前节点不为空
将当前节点压入栈
移动到左子节点

b. 节点处理
如果当前节点为空(即已经到达最左端)
从栈中弹出一个节点
将该节点的值添加到结果列表中
将当前节点移动到右子节点

3.返回结果列表

这种方法模拟了递归的过程:

首先尽可能深入地遍历左子树
然后处理节点本身
最后遍历右子树
使用栈来保存已经遍历过的父节点,以便在处理完左子树后能够回溯。

还是按代码思维走一遍,又和自己想的还是有一点区别。

class Solution {public List<Integer> inorderTraversal(TreeNode root) {//结果集List<Integer> result = new ArrayList<>();//特判if(root==null){return result;}//辅助栈Deque<TreeNode> st = new ArrayDeque<>();//用一个遍历指针指向rootTreeNode cur = root;//这里就是进递归处理的逻辑,循环条件决定了能不能递归处理下去。//cur!=null表明这个元素不空,可以进栈,!st.isEmpty(),是用来回溯的,表明栈中还有走过的元素需要进行处理。所以栈中走过的元素没处理完也要继续处理。while(cur!=null || !st.isEmpty()){//根据中序,左根右的走法,需要一路向最左下走。走的过程就一直入栈,保存之前的状态。如果cur==null,说明走到最左下的节点的左分支上了,也就是null。if(cur!=null){st.push(cur);cur = cur.left;}else{//不能继续往左走了才处理这里,这里就是开始回溯,回溯一下回到最左下的节点,此节点并不是null。那就把这个元素出栈,把这个元素的val加入结果集。由于每个元素都要左根右,这里处理了根节点,那还要往右节点走。下一波显然cur还是null,那么此时就会弹出沿着路线返回的根节点,往后都是这样。注意是依靠弹出的节点进行转移,因为栈里面的节点才是记录先前的状态,别自己瞎写一个。cur = st.pop();result.add(cur.val);cur = cur.right;}}//当st里面为空的时候,就是所有节点都处理完的时候。return result;}
}

102.二叉树的层序遍历

用队列。
看一个图就懂了。请添加图片描述
队列先进先出,符合一层一层遍历的逻辑。
下面的代码就是二叉树层序遍历的模板题,会这个模板,之后遇到只要是层序遍历的题,随便杀。

总的来说,这个解法看完,里面的len的处理我是当时没想到的。

class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>();checkFun(root,result);return result;}public void checkFun(TreeNode node,List<List<Integer>> result){//特判if(node==null){return;}//辅助队列Deque<TreeNode> que = new ArrayDeque<>();//根节点先入队que.offerLast(node);//队列不空就代表流程还没有结束while(!que.isEmpty()){//记录一层元素的结果集List<Integer> itemList = new ArrayList<Integer>();//在一开始先记录长度,该长度即队列目前的长度,就是该层元素的个数。//记录len就是为了做一个界限,即处理完这一层元素后,就要收集一次结果集。int len = que.size();//这个循环做依次将该层元素出队,并扩展其左右节点加入队列当中。while(len>0){//先出队TreeNode tmpNode = que.pollFirst();//加入结果集itemList.add(tmpNode.val);//扩展左右节点,加入队列中。if(tmpNode.left!=null){que.offerLast(tmpNode.left);}if(tmpNode.right!=null){que.offerLast(tmpNode.right);}//做完之后该层元素数量要-1len--;}//处理完一层后记录一次结果。result.add(itemList);}}
}

107.二叉树的层序遍历Ⅱ

在上题的基础上将结果集翻转就结束了。

class Solution {public List<List<Integer>> levelOrderBottom(TreeNode root) {List<List<Integer>> result = new ArrayList<>();checkFun(root,result);Collections.reverse(result);return result;}public void checkFun(TreeNode node,List<List<Integer>> result){if(node==null){return;}Deque<TreeNode> que = new ArrayDeque<>();que.offerLast(node);while(!que.isEmpty()){List<Integer> itemList = new ArrayList<Integer>();int len = que.size();while(len>0){TreeNode tmpNode = que.pollFirst();itemList.add(tmpNode.val);if(tmpNode.left!=null){que.offerLast(tmpNode.left);}if(tmpNode.right!=null){que.offerLast(tmpNode.right);}len--;}result.add(itemList);}}
}

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

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

相关文章

第二证券:销量暴跌95%,这一巨头市值蒸发超3000亿元!

在多重要素刺激下&#xff0c;PCB工作站上风口。 波音销量堕入停滞 6月仅售出3架客机 据央视财经&#xff0c;在一系列丑闻的影响下&#xff0c;波音公司本年出售遭到明显冲击。当地时间9日&#xff0c;波音发布的数据闪现&#xff0c;在以前一个月&#xff0c;该公司仅卖出…

关于Java面向对象的一些问题(2024.7.10)

package question20240710;public class Question {/*1. 什么叫做多态&#xff0c;条件是什么&#xff1f;2. 使用多态特性&#xff0c;带来了什么样的好处&#xff1f;3. 使用多态特性&#xff0c;注意什么样的弊端&#xff1f;4. 关于多态的弊端我们如何解决&#xff1f;5. 在…

excel有条件提取单元格特定文本(筛选纯文字的单元格或含有数字的单元格、单元格提取不同的文本长度)

实际工作背景 需要对导出的银行流水中的数十个村以及对应的村小组进行分组统计&#xff0c;但是初始的表格中村和小组是混在一起的&#xff0c;如下图所示&#xff1a; 目的&#xff1a;将大树村和大树村小组名称分别筛选出来 1.观察发现&#xff0c;大树村小组的单元格第4…

代码随想录算法训练营第四十九天| 647. 回文子串、 516.最长回文子序列

647. 回文子串 题目链接&#xff1a;647. 回文子串 文档讲解&#xff1a;代码随想录 状态&#xff1a;不会 思路&#xff1a; dp[i][j] 表示字符串 s 从索引 i 到索引 j 这一段子串是否为回文子串。 当s[i]与s[j]不相等&#xff0c;那没啥好说的了&#xff0c;dp[i][j]一定是fa…

构建与操作共享栈

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝既然选择了远方,当不负青春,砥砺前行! 共享栈是一种优化的栈实现方式,它允许两个或多个栈共享同一段连续的内存空间…

Tkinter 部件使用教程

tkinter学习教程 C语言中文网Tkinter教程 菜鸟编程-Python GUI编程(Tkinter) tkinter基本组件 messagebox 【tkinter标准对话框】messagebox&#xff1a;信息传递&#xff0c;消息对话框&#xff01; bind bind事件信息 listbox Tkinter 组件详解之Listbox radiobutton Tkinter…

数据结构——Trie

题目&#xff1a; 维护一个字符串集合&#xff0c;支持两种操作&#xff1a; I x 向集合中插入一个字符串 x&#x1d465;&#xff1b;Q x 询问一个字符串在集合中出现了多少次。 共有 N&#x1d441; 个操作&#xff0c;所有输入的字符串总长度不超过 10^5&#xff0c;字符串仅…

【分布式系统】Ceph对象存储系统之RGW接口

目录 一.对象存储概述 二.创建RGW接口 1.在管理节点创建一个 RGW 守护进程 2.创建成功后默认情况下会自动创建一系列用于 RGW 的存储池 3.默认情况下 RGW 监听 7480 号端口 4.开启 httphttps &#xff0c;更改监听端口 5.在 rgw 节点上查看端口 6.在客户端访问验证 7.…

Mybatis study

一、Mybatis Plus mybatis-plus指定实体类字段不查询 加标签 TableField(exist false) Spring Data Jpa学习 干我们这行&#xff0c;啥时候懈怠&#xff0c;就意味着长进的停止&#xff0c;长进的停止就意味着被淘汰&#xff0c;只能往前冲&#xff0c;直到凤凰涅槃的一天&am…

【onnx】onnxruntime-gpu无法使用问题

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 onnxruntime-gpu无法使用 1. 正文 CUDA版本&#xff1a;12.1 nvcc -VCUDNN的版本 cat /usr/include/cudnn_version.h |grep CUDNN_MAJOR -A 2说明: 可…

C#中的Dictionary

Dictionary<TKey, TValue> 是一个泛型集合&#xff0c;它存储键值对&#xff08;key-value pairs&#xff09;&#xff0c;其中每个键&#xff08;key&#xff09;都是唯一的。这个集合类提供了快速的数据插入和检索功能&#xff0c;因为它是基于哈希表实现的。 注意 ke…

拉曼操作维护使用手册(中英文对照)

1 INTRODUCTION 介绍 This document contains information needed to install and operate the Laser Gas Analyzer (LGA). The information contained herein is believed to be accurate and reliable, however, inaccuracies and omissions of pertinent information are po…

Vue 3 组件通信全解:从基础到高级技巧

引言 Vue 3 引入了 Composition API&#xff0c;这为组件通信带来了新的灵活性和强大的功能。 组件通信基础 组件的定义和作用 在前端开发中&#xff0c;组件可以被看作是构建用户界面的独立单元。它封装了特定的功能和样式&#xff0c;可以被重复使用&#xff0c;并且可以…

【数据结构——链表的深度探索】从实现到应用,保姆级攻略

【数据结构——链表深度探索】从实现到应用&#xff0c;保姆级攻略 &#x1f341;1. 链表的介绍&#x1f341;2. 链表的实现&#x1f341;2.1 单向链表&#x1f341;2.1.1 size()&#x1f341;2.1.2 display()&#x1f341;2.1.3 contains(int key)&#x1f341;2.1.4 addFirst…

墨西哥:海外新闻稿媒体分发-海外pr发稿干货分享-大舍传媒

大舍传媒&#xff1a;海外新闻稿媒体分发平台 墨西哥观查者 (mexicoviewer) 墨西哥观查者是墨西哥一家知名的新闻媒体平台&#xff0c;该平台专注于报道墨西哥国内外的时事新闻、政治、经济、文化等多个领域的内容。其更新速度快&#xff0c;报道对象广泛&#xff0c;深受墨西…

微信小程序---模板语法

一、声明和绑定数据 小程序页面中使用的数据均需要在 Page() 方法的 data 对象中进行声明定义 在将数据声明好以后&#xff0c;需要在 WXML 中绑定数据&#xff0c;数据绑定最简单的方式是使用 Mustache 语法&#xff08;双大括号&#xff09;将变量包起来。 在 {{ }} 内部可…

开始性能测试之前的准备工作!

性能测试是软件测试中不可或缺的一部分&#xff0c;它可以帮助我们评估软件系统的性能表现&#xff0c;并找出潜在的性能瓶颈。在进行性能测试之前&#xff0c;需要做好充分的准备工作&#xff0c;以确保测试的有效性和准确性。 1. 确定性能测试的目标和范围 * 明确测试目标:性…

《数据库原理》SQLServer期末复习_题型+考点

目录 题型&#xff1a; 一. 概况分析题&#xff08;5小题&#xff0c;每小题2分&#xff0c;共10分&#xff09; 二. 计算题&#xff08;3小题&#xff0c;每小题5分&#xff0c;共15分&#xff09; 三. 数据库设计&#xff08;2小题&#xff0c;每小题10分&#xff0c;共2…

什么是数组,什么是对象,并说出他们的区别

数组就是一组数据的集合。 对象就是用来储存变量的。 创建方式不同&#xff1a; 对象可以通过new关键字创建对象&#xff0c;或者通过对象字面量创建 数组&#xff1a;new Array() 数组表 示有序数据的集合&#xff0c;而对象表示无序数据的集合 数组的数据没有名称&#xff08…

在mysql中delete和truncated的相同点和区别点

相同点 删除数据&#xff1a;两者都会删除表中的数据。影响数据&#xff1a;两者都不删除表结构&#xff0c;只影响表中的数据。 区别点 操作方式&#xff1a; DELETE&#xff1a;逐行删除数据&#xff0c;可以使用 WHERE 子句来指定删除的条件。如果不加 WHERE 子句&#…