tag二叉树-刷题预备知识-2. 二叉树的前中后序遍历递归, 迭代, Java实现 +lt.144. 二叉树的前序遍历 +lt.94. 二叉树的中序遍历 + lt.145. 二叉树的后序遍历
文章目录
- 在本文章中, 将会阐述二叉树的三种遍历方式的递归和迭代写法;
- 之前我们写过: tag二叉树-刷题预备知识-1. 深入浅出深度优先遍历(DFS)和广度优先遍历(BFS), 建议反复阅读几遍, 你会懂得,
- 递归和深度优先遍历, 队列/栈和广度优先遍历几乎就是相辅相成的, 而且在你熟练掌握了DFS和BFS之后, 对于这种遍历的题目其实就能够信手拈来了;
- 可能会有些朋友对二叉树的迭代比较发怵, 先学学上文的BFS!
lt.144. 二叉树的前序遍历
[案例需求]
1. 前序遍历的递归法实现
[思路分析一, 递归解法]
[代码实现]
//1. 前序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> list = new ArrayList<>();
//1. 递归函数参数是看作是根节点的当前节点, 返回值为空,
public List<Integer> preorderTraversal(TreeNode root) {
//2. 递归结束条件
if(root == null)return list;
//3. 单层递归逻辑
// 因为是前序遍历, 所以遍历和处理节点是同步进行的,
// 即遍历一个根节点, 就要对这个节点进行处理
list.add(root.val);
preorderTraversal(root.left); //处理root(当前节点)的左子树
preorderTraversal(root.right); //处理root(当前节点)的右子树
return list; //4. 应该给函数返回的值
}
}
2. 前序遍历的迭代法实现
↑ BFS的队列改为了栈, 并反转了当前节点左右孩子存放顺序.
[思路分析二, 迭代解法]
- 上面的BFS和DFS你看了吗?
- 对于BFS遍历, 我们通常使用队列去实现, 因为BFS是按照每一层进行遍历的, 在添加遍历结点到队列时, 我们会把这个节点的子节点,
先放入到队列缓存
, 在遍历完某一层A的结点之后, 下一层B的所有元素也就已经在队列里等着我们啦 - 因为是队列的原因, 所以下一层B的结点顺序和我们此时从队列中取元素的顺序是一致的! 所以在本道题中我们就需要变通一下啦,
- 看上图BFS的动图, 可以看出, 当我们使用队列存取元素时, 对二叉树的遍历是层次遍历, 不符合前序遍历的需求, 问题就差在我们用的是队列存入元素,
如果我们把队列改为栈, 同时颠倒一下存入栈的左右子树的位置, 就是对二叉树的前序遍历啦!
由于“中左右”的访问顺序正好符合根结点寻找子节点的顺序,因此每次循环时弹栈,输出此弹栈结点并将其右结点和左结点按照叙述顺序依次入栈。至于为什么要右结点先入栈,是因为栈后进先出的特性。右结点先入栈,就会后输出右结点。
[代码实现]
你可以去前面提到的DFS文章中去比较一下, 是不是特别像! 一通百通!
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
//前序遍历, 迭代法
List<Integer> list = new ArrayList<>();
//特判
if(root == null) return list;
//相比于BFS标准实现, 我们应该用栈存入结点
Stack<TreeNode> st = new Stack<>();
st.push(root);
while(! st.isEmpty()){
root = st.pop();
list.add(root.val);
//注意, 由于栈的特性(先进后出), 为了实现前序遍历, 我们应该先添加右子树结点,再添加左子树结点
if(root.right != null) st.push(root.right);
if(root.left != null) st.push(root.left);
}
return list;
}
}
lt.94. 二叉树的中序遍历
[案例需求]
1. 中序遍历的递归法实现
[思路分析一, 递归解法]
[代码实现]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
//1. 递归函数参数: 被看做是根节点的当前节点, 返回的是存储结点的集合
List<Integer> list = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
//2. 递归出口, 树为空, 返回空list
if(root == null)return list;
//3. 单层递归逻辑
// 中序遍历, 先递归到左子树的末尾, 才会对接点进行处理
inOrder(root.left, list); //不断的递进, 即root的left的left的left
list.add(root.val); //上面的递进结束了, 在这个方法下面是归来过程, list开始存入归来时遇到的所有结点的值
inOrder(root.right, list); // 上面的左子树存完了, 继续往右子树进行递进, 注意哦 由于list.add在这个递归调用的上面, 所以list和这个inorder是一起递进的, 也就是这个inorder和list.add同时进行;
return list;
}
}
2. 中序遍历的迭代法实现
⬆ 栈负责临时存储, list负责左子树上的结点为空时进行处理
[思路分析二, 迭代解法]
[代码实现]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
//BFS
//递归的调用过程是不断往左边走,当左边走不下去了,就打印节点(存储结点等处理结点的过程),并转向右边,然后右边继续这个过程
List<Integer> list = new ArrayList<>();
Stack<TreeNode> st = new Stack<>();
while(!st.isEmpty() || root != null){
//一直向左遍历并存储遇到的结点
while(root != null){
st.push(root);
root = root.left;
}
//走到左边的最下边了
root = st.pop();
list.add(root.val);
//出栈的同时也要光顾一下当前出栈结点的右子树(或右子节点)
root = root.right;
}
return list;
}
}
lt.145. 二叉树的后序遍历
[案例需求]
1. 后序遍历的递归法实现
[思路分析一, 递归实现]
- lue
[代码实现一]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
//1. 函数的参数是当前节点(看作是根节点), 返回值是负责存储遍历结点的list
List<Integer> list = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
// 2. 递归结束条件
if(root == null)return list;
//3. 单层递归逻辑
postorderTraversal(root.left);
postorderTraversal(root.right);
list.add(root.val);
return list;
}
}
[代码实现二]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
postOrder(root, list);
return list;
}
public void postOrder(TreeNode root, List<Integer> list){
//递归终止条件
if(root == null)return;
//
postOrder(root.left, list);
postOrder(root.right, list);
//单层递归逻辑
list.add(root.val);
}
}
2. 后序遍历的迭代法实现
[思路分析二, 迭代法]
⬆ 与前序迭代比较像, 在它基础上, 先存的left后存的right, 然后整体进行反转
[代码示例]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
//bfs
List<Integer> list = new ArrayList<>();
Stack<TreeNode> st = new Stack<>();
if(root == null) return list;
st.push(root);
while(! st.isEmpty()){
root = st.pop();
list.add(root.val);
if(root.left != null)st.push(root.left);
if(root.right != null)st.push(root.right);
}
Collections.reverse(list);
return list;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律