【数据结构——树】二叉树的遍历(前序、中序、后序、层序)迭代+递归

news2025/1/12 7:55:47

文章目录

    • 二叉树的定义
    • 二叉树的遍历方式
      • 前序遍历
        • 递归DFS
        • 迭代(栈)
      • 中序遍历
        • 递归DFS
        • 迭代(栈)
      • 后序遍历
        • 递归DFS
        • 迭代(栈)
      • 层序遍历
        • 迭代(队列)

二叉树的定义

二叉树是一种常见的树状数据结构,它由一个称为根节点(Root)的节点和最多两个指向其他节点的指针(左子节点和右子节点)组成。

    static class TreeNode{
        public char val;//节点值
        public TreeNode left;//左孩子节点
        public TreeNode right;//右孩子节点
        
        public TreeNode(char val){//节点赋值
            this.val = val;
        }
        
        public TreeNode(char val,TreeNode left,TreeNode right){//节点赋值的同时,指定左右孩子
            this.val = val;
            this.left =left;
            this.right =right;
        }
    }

二叉树的遍历方式

  • 创建二叉树:
    public static TreeNode creatTree(){
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');

        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;

        return A;
    }

图示为:
在这里插入图片描述

前序遍历

前序遍历(根左右) A B D E H C F G

递归DFS

 //全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    
     // dfs  深度优先递归
    private static void dfs(TreeNode root) {
        if(root == null) return;//用于判空,也做为递归出口
        list.add(root);//根
        dfs(root.left);//左
        dfs(root.right);//右
    }

迭代(栈)

方式一

 //全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    /**
     * 迭代法 1 + 栈
     * 前序遍历是根左右,首先保存根节点,然后出栈,然后将值入list。
     * 然后入右节点、入左节点再重新进行循环,
     * 即将左节点当做根节点进行操作(即操作左子树),操作完左子树之后再操作右子树。
     */
    private static void iteration1(TreeNode root) {
        if (root == null) return;
        Deque<TreeNode> stack= new LinkedList<>();

        stack.push(root);// 将根节点入栈

        while(!stack.isEmpty()){
             root = stack.pop();//弹出遍历的节点
            list.add(root);
            // 先将右子节点入栈,再将左子节点入栈,这样出栈时就会先访问左子节点
            if(root.right != null) stack.push(root.right);
            if(root.left != null) stack.push(root.left);
        }
    }

方式二(推荐)

//全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    /**
     * 迭代法  2  + 栈
     * 入根然后一直入左,直到没有左,然后出栈顶(找到最左的节点),
     * 再然后找到最左的节点的右孩子,此时右孩子为根节点。然后循环操作。
     * 要点:根节点、左节点处理完之后,把右节点当做根节点然后又从循环开头开始操作(即整理整个右子树)。
     */
    private static void iteration(TreeNode root) {
        if (root == null) return;
        Deque<TreeNode> stack= new LinkedList<>();
        while(!stack.isEmpty() || root != null){
            while(root != null){  // 左节点一直入栈同时加入到list
                list.add(root);
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            root = root.right;//切换右节点继续循环
        }
    }

中序遍历

中序遍历(左根右) D B E H A F C G

递归DFS

	//全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    /**
     * dfs 递归 中序遍历
     *
     */
    private static void dfs(TreeNode root) {
        if(root == null) return;
        dfs(root.left);
        list.add(root);
        dfs(root.right);
    }

迭代(栈)

方式一

	//全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    /**
     * 迭代方式一 +栈
     */
    private static void iteration(TreeNode root) {

        if (root == null) return;

        Deque<TreeNode> stack = new LinkedList<>();

        while(!stack.isEmpty() || root != null){//注意  栈可能为空 此时root的左子树都遍历完了  继续遍历root.right  所以要加条件root != null
            if (root != null) { // 指针来访问节点,访问到最底层
                stack.push(root);// 将访问的节点放进栈
                root = root.left; // 左
            }else {
                root = stack.pop();
                list.add(root); // 中
                root = root.right; // 右
            }
        }

方式二(推荐)

	//全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    /**
     * 迭代方式二 +栈
     */
    private static void iteration1(TreeNode root) {
        if (root == null) return;

        Deque<TreeNode> stack = new LinkedList<>();

        while(!stack.isEmpty() || root != null) {//注意  栈可能为空 此时root的左子树都遍历完了  继续遍历root.right  所以要加条件root != null
                while(root != null){
                    stack.push(root);
                    root = root.left;//访问左子树节点到最底层
                }
                root = stack.pop();//若节点左子树为null 则弹出 加入list
                list.add(root);
                root = root.right;//接着访问弹出节点的左子树
        }
    }

后序遍历

后序遍历(左右根) D H E B F G C A

递归DFS

//全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
        /**
     * dfs 后序递归(左右根)  D  H   E   B   F   G   C   A
     */
    private static void dfs(TreeNode root) {
        if(root == null) return;
        dfs(root.left);//左
        dfs(root.right);//右
        list.add(root);//根
    }

迭代(栈)

方式一

//全局list集合 //存放树的节点
    static List<TreeNode> list = new ArrayList<>();
    /**
     * 迭代方式一 + 栈 (在前序遍历上改良  交换前序遍历的左右孩子入栈的顺序  得到  根右左  然后再逆转过来就是后序遍历
     */
    private static void iteration1(TreeNode root) {
        if(root == null) return;
        Deque<TreeNode> stack = new LinkedList<>();
        stack.push(root);
        while(!stack.isEmpty() ){
             root = stack.pop();
             list.add(root);
             if(root.left != null) stack.push(root.left);//相对于前序遍历,这更改一下入栈顺序  使得右节点率先出栈  (根右左--->左右根)
             if(root.right != null) stack.push(root.right);
        }
        //上面得到的其实就是后序遍历的逆序   所以只要把list逆过来就是后序遍历了 (根右左--->左右根)
        Collections.reverse(list);
    }

方式二(推荐)

 /**
     * 迭代方式二 + 栈
     * 中左一直入栈,直到没有左边,然后查找栈顶节点是否有右节点,没有则出栈入vector,
     * 有则将右节点作为根节点重新循环(即将右边那部分直接当做一棵树)。
     */
    private static void iteration(TreeNode root) {
        if (root == null) {
            return ;
        }

        Stack<TreeNode> stack = new Stack<>();
        TreeNode prev = null;
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
                TreeNode peekNode = stack.peek();
                if (peekNode.right != null &&  peekNode.right!= prev) {
                    // 如果右子节点存在且未被访问过,则处理右子树
                    root = peekNode.right;
                } else {
                    // 否则,说明左右子树都已经处理完毕,可以访问当前节点
                    list.add(peekNode);
                    prev = stack.pop();//记录弹出的节点  用于判断下次处理节点时  右孩子节点是否处理过
                }

        }
    }

层序遍历

层序遍历 A B C D E F G H

迭代(队列)

/**
     * 迭代 + 队列
     * @param root
     */
    private static void iteration(TreeNode root) {
        if(root == null) return;
        Queue<TreeNode> queue = new LinkedList<>();//队列
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();//记录每层的节点个数
            for (int i = 0; i < size; i++) {//取出每层的节点
                root = queue.poll();
                list.add(root);
                if(root.left != null) queue.offer(root.left);//如果当前节点的孩子节点不为空则加入
                if(root.right != null) queue.offer(root.right);
            }
        }

    }

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

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

相关文章

MySQL表的增删查改以及基本查询样例

文章目录 表的增删查改创建表插入单行全列数据插入多行指定列数据插入失败则更新替换 select全列查询指定列查询查询字段为表达式为查询结果指定别名查询结果去重 where数学小于60的英语在70到100之间的名字为王开头的总分在 200 分以下的语文成绩 > 80 并且不姓王的 结果排…

IDEA设置文件编码

IDEA设置文件编码 File->Settings->Editor->File Encodings 均设置为utf-8 新项目 设置 文件编码 点击New Projects Setup 再点击Settings for New Projects File->Settings->Editor->File Encodings 均设置为utf-8

Spring-TX 事务

目录 一、事务的种类 二、Spring事务管理器 三、事务注解使用 四、事务注解属性 一、事务的种类 1.编程式事务 所谓编程式事务就是用代码手写事务&#xff0c;包含了事务的开始&#xff0c;具体事务&#xff0c;事务的提交和事务的回滚。在这期间就会产生一些冗余问题&am…

Redis 7 第六讲 主从模式(replica)

🌹🌹🌹 此篇开始进入高级篇范围(❤艸`❤) 理论 即主从复制,master以写为主,Slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。 使用场景 读写分离 容灾备份数据备份水平扩容主从架构 演示案例 注:masterauth、replicaof主…

pytorch如何使用Focal Loss

Focal loss 是 文章 Focal Loss for Dense Object Detection 中提出对简单样本的进行decay的一种损失函数。是对标准的Cross Entropy Loss 的一种改进。 FL对于简单样本&#xff08;p比较大&#xff09;回应较小的loss。 如论文中的图1&#xff0c; 在p0.6时&#xff0c; 标准的…

openGauss学习笔记-57 openGauss 高级特性-并行查询

文章目录 openGauss学习笔记-57 openGauss 高级特性-并行查询57.1 适用场景与限制57.2 资源对SMP性能的影响57.3 其他因素对SMP性能的影响57.4 配置步骤 openGauss学习笔记-57 openGauss 高级特性-并行查询 openGauss的SMP并行技术是一种利用计算机多核CPU架构来实现多线程并行…

使用Vue3和Vite升级你的Vue2+Webpack项目

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

十一、做高并发内存池项目过程中遇到的bug以及调试bug的方法和心得

十一、做高并发内存池项目过程中遇到的bug以及调试bug的方法和心得 第一个bug是内存问题&#xff0c;程序直接崩溃&#xff0c;问题出现在&#xff1a;GetOneSpan函数中的切分span的时候结尾的span1的next没有置空。 第二个bug是还小内存块给span的时候找不到小内存所属的spa…

深入理解 JVM 之——Java 内存区域与溢出异常

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 本篇为深入理解 Java 虚拟机第二章内容&#xff0c;推荐在学习前先掌握基础的 Linux 操作、编译原理、计算机组成原理等计算机基础以及扎实的 C/C 功底。 该系列的 GitHub 仓库&#xff1a;https://github…

LVS 实现四层负载均衡项目实战--DR模式(直接路由Direct Routing)

一、原理 负载均衡器和RS都使用同一个IP对外服务&#xff61;但只有DB对ARP请求进行响应,所有RS对本身这个IP的ARP请求保持静默&#xff61;也就是说,网关会把对这个服务IP的请求全部定向给DB,而DB收到数据包后根据调度算法,找出对应的RS,把目的MAC地址改为RS的MAC&#xff08…

MacOS 为指定应用添加指定权限(浏览器无法使用摄像头、麦克风终极解决方案)

起因&#xff1a;需要浏览器在线做一些测评&#xff0c;但我的 Chrome 没有摄像头/麦克风权限&#xff0c;并且在设置中是没有手动添加按钮的。 我尝试了重装软件&#xff0c;更新系统&#xff08;上面的 13.5 就是这么来的&#xff0c;我本来都半年懒得更新系统了&#xff09…

Tomcat安装及环境配置

一、首先确认自己是否已经安装JDK WinR打开运行&#xff0c;输入cmd回车&#xff0c;在DOS窗口中输入java 出现这些说明已经安装了&#xff0c;然后就是查看自己的版本 然后输入java -version 可以看到版本是1.8的 如果没有配置java环境变量&#xff0c;打开系统设置&#…

Linux安装JenkinsCLI

项目简介安装目录 mkdir -p /opt/jenkinscli && cd /opt/jenkinscli JenkinsCLI下载 wget http://<your-jenkins-server>/jnlpJars/jenkins-cli.jar # <your-jenkins-server> 替换为你的 Jenkins 服务器地址 JenkinsCLI授权 Dashboard-->Configure Glob…

渣土车识别监测 渣土车未盖篷布识别抓拍算法

渣土车识别监测 渣土车未盖篷布识别抓拍算法通过yolov7深度学习训练模型框架&#xff0c;渣土车识别监测 渣土车未盖篷布识别抓拍算法在指定区域内实时监测渣土车的进出状况以及对渣土车未盖篷布违规的抓拍和预警。YOLOv7 的策略是使用组卷积来扩展计算块的通道和基数。研究者将…

交叉导轨具有那些方面的优势?

交叉导轨和直线导轨这两种导轨经常被拿出来对比&#xff0c;从结构上来看&#xff0c;交叉导轨是分体结构&#xff0c;组装起来相对于直线导轨比较繁琐&#xff1b;从功能上来看&#xff0c;交叉导轨适合于短行程&#xff0c;高频率、高精度的场合。直线导轨可以做到6000mm单支…

Paimon+StarRocks 湖仓一体数据分析方案

摘要&#xff1a;本文整理自阿里云高级开发工程师曾庆栋&#xff08;曦乐&#xff09;在 Streaming Lakehouse Meetup 的分享。内容主要分为四个部分&#xff1a; 传统数据仓库分析实现方案简介PaimonStarRocks 构建湖仓一体数据分析实现方案StarRocks 与 Paimon 结合的使用方式…

从零开始,掌握C语言中的数据类型

数据类型 1. 前言2. 预备知识2.1 打印整数2.2 计算机中的单位 3. C语言有哪些数据类型呢&#xff1f;3.1 内置类型和自定义类型 4. 每种类型的大小是多少&#xff1f;5. 为什么有这么多数据类型呢&#xff1f;6. 这么多类型应该如何使用呢&#xff1f;6.1 一个小知识 1. 前言 …

Redis功能实战篇之Session共享

1.使用redis共享session来实现用户登录以及token刷新 当用户请求我们的nginx服务器&#xff0c;nginx基于七层模型走的事HTTP协议&#xff0c;可以实现基于Lua直接绕开tomcat访问redis&#xff0c;也可以作为静态资源服务器&#xff0c;轻松扛下上万并发&#xff0c; 负载均衡…

【VR】Network Manager HUD

&#x1f4a6;本专栏是我关于VR开发的笔记 &#x1f236;本篇是——Network Manager HUD Network Manager HUD组件 简介基础知识 简介 网络管理器 HUD是一种快速启动工具&#xff0c;可帮助您立即开始构建多人游戏&#xff0c;而无需首先构建用于游戏创建/连接/加入的用户界面…

云原生Kubernetes:二进制部署K8S单Master架构(一)

目录 一、理论 1.K8S单Master架构 2. etcd 集群 3.flannel网络 4.K8S单Master架构环境部署 5.部署 etcd 集群 6.部署 docker 引擎 7.flannel网络配置 二、实验 1.二进制部署K8S单Master架构 2. 环境部署 3.部署 etcd 集群 4.部署 docker 引擎 5.flannel网络配置…