非递归方法实现二叉树前、中、后序遍历

news2024/12/24 12:51:52

文章目录

  • 非递归实现二叉树前、中、后序遍历
    • 一、非递归实现前序遍历
      • 1.思路
      • 2.代码
    • 二、非递归实现二叉树的中序遍历
      • 1.思路
      • 2.代码
    • 三、非递归实现二叉树的后序遍历
      • 1.思路
      • 2.代码


非递归实现二叉树前、中、后序遍历


一、非递归实现前序遍历

在这里插入图片描述

1.思路

  • 前序遍历的顺序是 :根——左——右

  • 模拟递归在栈上的执行过程

  • 用栈来实现

1.用cur代替root的移动,将当前cur压栈并打印

2.cur指向当前cur的左子树,cur.left压栈并打印,直到cur的左边遇到空为止

3.先序遍历时,根节点压栈先不着急取出,栈是先进后出的,先找根的左子树,后续会出栈来利用根节点找右子树,栈维护了根节点出现的顺序

4.当cur为空时,弹出栈顶结点,栈顶结点的左边是为空的cur,要利用栈顶结点来找他的右边

5.如果此时栈顶元素的右边也为空,该节点的根左右找完了,当前结点代表着上一个根结点的左树找完了

6.上一个根节点的根、左找完了,弹出栈顶元素(上一个结点),来找他的右边

  • 也就是说,先让cur来找根并打印,一直找下去,遇到空了找右边。当最小的左子树的根左右都找齐后,这个子树和它的上一级的根已经找到,需要开始找当前根的右边,形成循环。最后这个二叉树的左子树找齐后,开始找右子树。
  • 用两个while循环,小循环的条件是cur不为空,负责进栈,大循环嵌套小循环,小循环外负责出栈
  • 大循环的条件是:出栈结点的右边不为空,或者栈不为空

2.代码

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
         if (root==null){
            return res;
        }
        TreeNode cur = root;//cur指向root
        Deque<TreeNode> stack = new LinkedList<>();

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {//cur不等于空,进栈并打印
                stack.push(cur);
               // System.out.print(cur.val + " ");
               res.add(cur.val);
                cur = cur.left;//先找左树所有的根
            }
            TreeNode top = stack.pop();//cur为空了,出栈找结点的右边
            cur = top.right;//cur指向结点的右边,如果cur不为空,进入大循环,再次进小循环入栈
            //如果cur==null,则判断栈空不空,栈不为空,进入大循环,不进小循环,只出栈
        }
        return res;
    }

二、非递归实现二叉树的中序遍历

在这里插入图片描述

1.思路

1.和上面的前序遍历类似,不过中序遍历打印的顺序是: 左——根——右

2.所以在小循环中,cur找到左树的根后,进栈时不打印,等出栈时再打印

3.也就是说,找到叶子结点后,叶子结点左边为空时,弹出叶子结点并打印(左、根)

4.找叶子结点的右边,右边为空时,说明左中右已找完,

5.此时左边已打印完,cur为空,出栈并打印根节点,利用根节点找右边的结点

  • 打印的位置与前序遍历不同

2.代码

    public List<Integer> inorderTraversal(TreeNode root) {
       List<Integer> res = new ArrayList<>();
         if (root==null){
            return res;
        }
        TreeNode cur = root;//cur指向root
        Deque<TreeNode> stack = new LinkedList<>();

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {//cur不等于空,进栈并打印
                stack.push(cur);
               // System.out.print(cur.val + " ");
               
                cur = cur.left;//先找左树所有的根
            }
            TreeNode top = stack.pop();//cur为空了,出栈找结点的右边
            res.add(top.val);
            cur = top.right;//cur指向结点的右边,如果cur不为空,进入大循环,再次进小循环入栈
            //如果cur==null,则判断栈空不空,栈不为空,进入大循环,不进小循环,只出栈
        }
   
        
        return res;
    }

三、非递归实现二叉树的后序遍历

在这里插入图片描述

1.思路

  • 后序遍历的具体思路类似,但是有不同的地方

  • 前序遍历中,找结点的右边要出栈找,维护出栈顺序,出栈的那一刻起,左边之前已经为空或者打印了,出栈取到结点找到右边,如果有结点新的结点会进栈,如果为空说明子树应该找完了,此时跟出栈的结点无关了

  • 中序遍历只是打印的位置不一样,前序和中序遍历,要看结点的右边,需要出栈来查看,出栈的目的是,根和左已经找完了,只需要取出来找右边就可以了,右边找完根据栈返回上一级

    1.后续遍历的顺序: 左——右——根

    2.因为根是最后打印的,先要看左节点,所以根节点先不能出栈,用peek取到 存的根节点,找根节点的右边

    3.右边为空时,打印栈顶的结点,弹出栈顶,cur为空,进入大循环,查看栈顶的右边,不为空,改变cur的指向

    4.出栈有要求,所以会造成peek元素过后,再次找到已经打印的结点

    5.所以在打印的时候还要判断是否打印过该节点

2.代码

    public void postorderNor(TreeNode root) {
        if (root == null) {
            return;
        }
        TreeNode cur = root;//cur指向root
        TreeNode prev = null;//记录打印过的结点
        Deque<TreeNode> stack = new LinkedList<>();

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {

                cur = cur.left;//先找左树所有的根
            }
            TreeNode top = stack.peek();//根节点不出栈,用peek取到根节点

            if (top.right==null||top.right ==prev ){//排除打印过的元素
                //左边找到了,右边为空,打印栈中的结点
                System.out.print(top.val+" ");
                stack.push(top);//打印完后弹出
                //此时cur为空
                prev = top;//记录打印过的结点

            }else {
                cur = top.right;//右边不为空,cur指向右边
            }
        }
    }

点击移步博客主页,欢迎光临~

偷cyk的图

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

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

相关文章

【Linux】僵尸进程、孤儿进程的理解与验证

僵尸进程 概念 僵尸进程&#xff08;Zombie Process&#xff09;是指一个已经终止执行的子进程&#xff0c;但其父进程尚未调用 wait() 或 waitpid() 函数来获取子进程的退出状态。 Linux 中&#xff0c;僵尸进程会保留一些资源&#xff0c;如进程 ID、进程表项和一些系统资源…

C++11 initializer_list 轻量级初始化列表的使用场景(让自定义类可以用初始化列表的形式来实例化对象)

initializer_list 是 C11 中的一个特性&#xff0c;它允许你使用花括号 {} 中的值列表来初始化容器或数组。通常用于初始化标准库容器&#xff0c;比如 std::vector、std::set、std::map 以及数组。 场景一&#xff1a;用初始化列表初始化容器 std::vector<int> arr {…

JavaScript基础知识点速通

0 前言 本文是近期我学习JavaScript网课的笔记&#xff0c;一是方便自己速查回忆&#xff0c;二是希望帮到同样有需求的朋友们。 1 介绍 1.1 基本情况 JavaScript是一种编程语言&#xff0c;运行在客户端&#xff08;浏览器&#xff09;上&#xff0c;实现人机交互效果&…

【扩散模型】不同组件搭积木,获得新模型

学习地址&#xff1a; https://github.com/huggingface/diffusion-models-class/tree/main/unit3 VAE The Tokenizer and Text Encoder UNet In-Painting 例如&#xff1a;基于contrlnet做的校徽转图片

vue项目使用vite设置proxy代理,vite.config.js配置,解决本地跨域问题

vue3vite4项目&#xff0c;配置代理实现本地开发跨域问题 非同源请求&#xff0c;也就是协议(protocol)、端口(port)、主机(host)其中一项不相同的时候&#xff0c;这时候就会产生跨域 vite的proxy代理和vue-cli的proxy大致相同&#xff0c;需要在vite.config.js文件中配置&…

一、Linux开机、重启、关机和用户登录注销

1.【关机】 shutdown shutdown now 表示立即关机 shutdown -h now 表示立即关机 shutdown -h 1 表示1分钟后关机 halt 用来关闭正在运行的Linux操作系统 2.【重启】 shutdown -r now 表示立即重启 reboot 重启系统 sync …

设计模式之装饰模式--优雅的增强

目录 概述什么是装饰模式为什么使用装饰模式关键角色基本代码应用场景 版本迭代版本一版本二版本三—装饰模式 装饰模式中的巧妙之处1、被装饰对象和装饰对象共享相同的接口或父类2、当调用装饰器类的装饰方法时&#xff0c;会先调用被装饰对象的同名方法3、子类方法与父类方法…

中国人民大学与加拿大女王大学金融硕士—重要的是,你一直在努力

人虽然生下来就分三六九等&#xff0c;不同的人过着不同的生活&#xff0c;我的生活没办法选择&#xff0c;我只能尽我所能的让自己变得优秀。中国人民大学与加拿大女王大学金融硕士是我们无论怎样都可以变优秀的优质渠道。V13146152701 那么我们为什么要读研&#xff0c;读研…

串口通信代码整合1

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

OLE DB 访问接口所需的(最大)数据长度为 18,但返回的数据长度为 6。

sqlserver查询oracle链接服务器视图,报错 给最终返回的字符串进行类型转换,字符串大小按返回值最大的那个oracle源本字段类型长度 aaaaaa AS yljgbmcast(aaaaaa AS varchar(10)) AS yljgbm

Proteus仿真--1602LCD显示仿手机键盘按键字符(仿真文件+程序)

本文主要介绍基于51单片机的1602LCD显示仿手机键盘按键字符&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中左下角12个按键模拟仿真手机键盘&#xff0c;使用方法同手机键一样&#xff0c;长按自动跳动切换键值&#xff0c;松手后确认选择&#xff…

API接口加密,解决自动化中登录问题

一、加密方式 AES&#xff1a;对称加密&#xff0c;快RAS&#xff1a;非对称加密&#xff0c;慢AESRAS&#xff1a;安全高效 加密过程&#xff1a;字符串》字节流》加密的字节流&#xff08;算法&#xff09;&#xff0c;解密有可能出现乱码&#xff0c;所以不能直接转成字符…

python+selenium自动化测试--鼠标悬停浮窗定位

页面上有些元素会隐藏起来&#xff0c;要鼠标放到某个位置才会显示出来&#xff0c;例如百度首页https://www.baidu.com/设置下面的隐藏按钮&#xff0c;如下图所示 定位鼠标悬停才显示的元素&#xff0c;要引入新模块&#xff0c;如下所示 from selenium.webdriver.common.ac…

12.JavaScript(WebAPI) - JS api文献精解

文章目录 1.WebAPI 背景知识1.1什么是 WebAPI1.2什么是 API1.3API 参考文档 2.DOM 基本概念2.1什么是 DOM2.2DOM 树 3.获取元素3.1querySelector3.2querySelectorAll 4.事件初识4.1基本概念4.2事件三要素4.3简单示例 5.操作元素5.1获取/修改元素内容5.1.1innerText5.1.2innerHT…

AD9371 官方例程裸机SW 和 HDL配置概述(二)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

javafaker测试数据生成实战

javafaker测试数据生成实战 1.背景2.介绍2.1 特点 3. 使用3.1 基础使用3.1.1 maven依赖3.1.1 使用示例 3.2 进阶使用3.1 生成中文信息3.2 根据姓名生成账号3.2.1 maven依赖3.2.2 中文转拼音工具类 3.3 高级使用3.3.1 中文性名重复处理方案1: 偷懒方式方案2: 较真模式 1.背景 最…

ChatGPT 被爆重大隐私泄露!在回答时突然蹦出陌生男子自拍照,你的数据都将被偷走训练模型!

ChatGPT 被爆重大隐私泄露 &#xff01; 一位用户在向 ChatGPT 询问 Python 中的代码格式化包 black 的用法时&#xff0c;没有一点点防备&#xff0c;ChatGPT 在回答中插入了一个陌生男子的自拍照&#xff08;手动捂脸.jpg&#xff09; 可以看到刚开始 ChatGPT 还相当正常&am…

智慧灯杆网关智能化选择(网关助力城市完整项目方案)

在当代城市发展中&#xff0c;智慧照明作为一项重要的技术创新&#xff0c;正逐渐改变着我们的城市生活。作为城市智慧照明的核心设备&#xff0c;智慧灯杆网关SG600凭借出色的性能和创新的解决方案&#xff0c;成为了引领城市智慧照明的完美选择。本文将详细介绍SG600的特点和…

linux centos7安装colmap

centos安装colmap 一、安装依赖 sudo yum install \gflags-devel \glog-devel \glew-devel \atlas \atlas-devel \lapack-devel \blas-devel \flann-devel \lz4-devel \sqlite-devel \metis-devel \qt5-qtbase-devel二、编译安装colmap git clone https://github.com/colmap/…

剑指JUC原理-10.并发编程大师的原子累加器底层优化原理(与人类的优秀灵魂对话)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…