【Java数据结构】二叉树进阶——非递归实现前中后序遍历二叉树(深入理解二叉树)+进阶大厂面试题(一行一注释)上

简介: 笔记

🗽非递归实现遍历二叉树(深入理解二叉树)


  • 代码每行都有注释,可以一步一步的画着图走一走,多走几遍,理解会上一个档次!
  • 前序遍历和中序遍历都用到栈,代码可以说一模一样,只不过打印节点的时机不一样


⭐非递归前序遍历

// 非递归实现前序遍历
    public void FDG_reOrderTraversal(TreeNode root){
        if (root == null) {//先判断根节点是否空
            return;//第一个根节点如果空的话,就直接返回了
        }
        TreeNode cur = root;//设置临时节点cur,通过这个节点来遍历整棵树
        Stack<TreeNode> stack = new Stack<TreeNode>();//创建一个栈
        while(cur != null || !stack.isEmpty()) {//外循环,先写内循环采写外循环,只要栈不为空,说明还没遍历完成
            while (cur != null) {//内循环
                stack.push(cur);//将当前cur节点入栈
                System.out.print(cur.value+" ");//打印cur节点,前序遍历是在入栈后打印
                cur = cur.left;//cur走到原节点的左孩子节点
            }
            //当左子树走完,cur.left肯定会走到空节点,这时候就要走右子树了
            TreeNode top = stack.pop();//弹出栈顶节点,注意是弹出,不然会影响后遍历上一个节点的右孩子节点
            cur = top.right;//cur走到上一个节点的右孩子节点
        }
        //栈空了,说明遍历完了所有节点
        System.out.println();
    }

⭐非递归中序遍历

// 非递归实现中序遍历
    public void FDG_inOrderTraversal(TreeNode root) {
       if (root == null) {//先判断根节点是否空
            return;//第一个根节点如果空的话,就直接返回了
        }
        TreeNode cur = root;//设置临时节点cur,通过这个节点来遍历整棵树
        Stack<TreeNode> stack = new Stack<TreeNode>();//创建一个栈
        while(cur != null || !stack.isEmpty()) {//外循环,先写内循环采写外循环,只要栈不为空,说明还没遍历完成
            while (cur != null) {//内循环
                stack.push(cur);//将当前cur节点入栈,但不打印,需要用它来找左右孩子节点
                cur = cur.left;//cur走到原节点的左孩子节点
            }
            //当左子树走完,cur.left肯定会走到空节点,这时候就要走右子树了
            TreeNode top = stack.pop();//弹出栈顶节点,注意是弹出,不然会影响后遍历上一个节点的右孩子节点
            System.out.print(top.value+" ");//打印cur节点,中序遍历是在节点出栈后打印
            cur = top.right;//cur走到上一个节点的右孩子节点
        }
        //栈空了,说明遍历完了所有节点
        System.out.println();
    }

⭐非递归后序遍历

//非递归后续遍历
   public void postOrderTraversalNor(TreeNode root) {
        if(root == null) return;//第一个根节点如果空的话,就直接返回了
        TreeNode cur = root;//设置临时节点cur,通过这个节点来遍历整棵树
        Stack<TreeNode> stack = new Stack<>();//创建一个栈
        TreeNode pre = null;//用来指定 上一个被打印的元素
        while (cur != null || !stack.empty()) {//外循环,只要栈不为空,说明还没遍历完成
            while (cur != null) {//内循环
                stack.push(cur);//将当前cur节点入栈,但不打印,需要用它来找左右孩子节点
                cur = cur.left;//cur走到原节点的左孩子节点
            }
            cur = stack.peek();//查看栈顶节点
            if (cur.right == null || cur.right == pre) {//如果栈顶节点的右节点为空,或者已经遍历过了
                TreeNode popNode = stack.pop();//弹出栈顶节点并打印
                System.out.print(popNode.value + " ");
                pre = cur;//用pre将遍历(打印)过的节点记录下来
                cur = null;//cur要置空,不然就又来一遍了,置空后可以继续查看下一个栈顶节点而不进入内循环
            } else {//若栈顶节点右节点不为空
                cur = cur.right;//则遍历这个右节点
            }
        }
        System.out.println();
    }


🗽大厂OJ面试题


🎄1. 二叉树的构建及遍历


题目:

40.png

思路:

  1. 本题思路很简单,就是遍历字符串,因为这是根据前序遍历搞出来的字符串
  2. 所以构建二叉树,也要根据这个 根->左节点->右节点 的顺序来构建

41.png

实现代码:

import java.util.*;
//题目啥也没给,节点类也要自己定义一下
class TreeNode{
    char value;//节点有值
    TreeNode left;//有左孩子节点
    TreeNode right;//有右孩子节点
    public TreeNode(char value){//构造函数,用于给节点赋值
        this.value = value;
    }
}
public class Main{
 //主函数,用于输入字符串,主要在creatTree方法里来实现
 public static void main(String[]args){
        Scanner scn = new Scanner(System.in);
        String str = scn.nextLine();
        TreeNode root =  creatTree(str);
        inOrderTraveled(root);
    }
    public static int i = 0;//i用于记录字符串中字符的下标
    //因为构建过程是递归的,不能用局部变量,所以要在外部设置成静态的
    public static TreeNode creatTree(String str){
        if(str==null){//如果字符串为空
            return null;//直接返回null
        }
        //字符串不为空,就进行构构建二叉树的过程
        TreeNode root = null;//先创建一个根节点,指向空,这样做是为了初始化
        if(str.charAt(i)!='#'){//依次读取字符串中的字符,不为 # 就进行二叉树构造
            root = new TreeNode(str.charAt(i));//将读取的字符给节点实例化
            i++;//当前读取的字符被用过之后,字符串下标要往后走一位
            root.left = creatTree(str);//递归构建左树
            root.right = creatTree(str);//递归构建右树
        }else{//读取到的字符是 # ,代表空节点,不用构建
            i++;//字符串下标往后走一位
        }
        return root;//最后返回根节点即可
    }
    //对构建好的二叉树进行中序遍历,用递归实现就好了
    public static void inOrderTraveled(TreeNode root){
        if(root==null) return;
        inOrderTraveled(root.left);
        System.out.print(root.value+" ");
        inOrderTraveled(root.right);
    }
}


🎄2. 二叉树的分层遍历


题目:

42.png

思路:

  1. 层序遍历就是一层一层的遍历节点

43.png


2.这题还有一个要求就是,输出的时候将一层的节点放在一行,下一层的节点放在下一行,这就需要用到一个二维数组来储存每一层的节点


44.png

3.我们先来观察一下 层序遍历的过程中,结点进队列和出队列的过程: 请看图

46.gif



4.可是如何知道当前访问到的节点是哪一层的呢? 截取 BFS 遍历过程中的某个时刻:

47.png


5.可以看到,此时队列中的结点是 3、4、5,分别来自第 1 层和第 2 层。这个时候,第 1 层的结点还没出完,第 2 层的结点就进来了,而且两层的结点在队列中紧挨在一起,我们无法区分队列中的结点来自哪一层。

因此,我们在每一层遍历开始前,先记录队列中的结点数量 n(也就是这一层的结点数量),然后一口气处理完这一层的 n 个结点。

48.gif


实现代码:

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
     //建立一个二维数组来记录每一层的情况
     List<List<Integer>> list = new ArrayList();
     Queue<TreeNode> queue = new LinkedList<>();//用队列实现层序遍历
        if (root==null){
            return list;
        }
        queue.offer(root);//根节点先入队
        while(!queue.isEmpty()) {
            int n = queue.size();//记录每一层有多少个节点,循环n次
            List<Integer> level = new ArrayList();//每一层用一个数组记录
            for(int i = 0 ; i<n ; i++){//弹出当前层里的节点,
            // 变量 i 无实际意义,只是为了循环 n 次
                TreeNode top = queue.poll();//弹出队头节点
                level.add(top.val);//将弹出的节点加入它所在的那一层
                //弹出的时候不要忘了将弹出节点的孩子节点入队
                if (top.left != null) {
                    queue.offer(top.left);
                }
                if (top.right != null) {
                    queue.offer(top.right);
                }
            }
            list.add(level);//将每一层添加到二维数组中
        }
        return list;//最后返回二维数组即为所求
    }
}



相关文章
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
543 1
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
388 59
|
10月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
475 3
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
4677 113
|
存储 监控 Java
《从头开始学java,一天一个知识点》之:数组入门:一维数组的定义与遍历
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。明日预告:《多维数组与常见操作》。 通过实例讲解数组的核心认知、趣味场景应用、企业级开发规范及优化技巧,帮助你快速掌握Java数组的精髓。
447 23
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
1058 1
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
637 3
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
277 5
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
376 6
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
下一篇
开通oss服务