二叉树题目:二叉树的后序遍历

news2024/9/23 19:27:51

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
      • 进阶
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法三
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:二叉树的后序遍历

出处:145. 二叉树的后序遍历

难度

3 级

题目描述

要求

给你二叉树的根结点 root \texttt{root} root,返回其结点值的后序遍历。

示例

示例 1:

示例 1

输入: root   =   [1,null,2,3] \texttt{root = [1,null,2,3]} root = [1,null,2,3]
输出: [3,2,1] \texttt{[3,2,1]} [3,2,1]

示例 2:

输入: root   =   [] \texttt{root = []} root = []
输出: [] \texttt{[]} []

示例 3:

输入: root   =   [1] \texttt{root = [1]} root = [1]
输出: [1] \texttt{[1]} [1]

数据范围

  • 树中结点数目在范围 [0,   100] \texttt{[0, 100]} [0, 100]
  • -100 ≤ Node.val ≤ 100 \texttt{-100} \le \texttt{Node.val} \le \texttt{100} -100Node.val100

进阶

递归解法很简单,你可以使用迭代解法完成吗?

解法一

思路和算法

二叉树的后序遍历的方法为:依次遍历左子树、右子树和根结点,对于左子树和右子树使用同样的方法遍历。由于遍历过程具有递归的性质,因此可以使用递归的方法实现二叉树的后序遍历。

递归的终止条件是当前结点为空。对于非终止条件,递归的做法如下。

  1. 对当前结点的左子树调用递归。

  2. 对当前结点的右子树调用递归。

  3. 将当前结点的结点值加入后序遍历序列。

遍历结束之后即可得到后序遍历序列。

代码

class Solution {
    List<Integer> traversal = new ArrayList<Integer>();

    public List<Integer> postorderTraversal(TreeNode root) {
        postorder(root);
        return traversal;
    }

    public void postorder(TreeNode node) {
        if (node == null) {
            return;
        }
        postorder(node.left);
        postorder(node.right);
        traversal.add(node.val);
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是递归调用的栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)

解法二

思路和算法

使用迭代的方法实现二叉树的后序遍历,则需要使用栈存储结点。

相比于前序遍历和中序遍历,后序遍历的迭代较为复杂。由于后序遍历对于每个子树都是最后访问根结点,因此需要记录上一个访问的结点,才能确保不会重复访问同一个结点。

从根结点开始遍历,遍历的终止条件是栈为空且当前结点为空。遍历的做法如下。

  1. 如果当前结点不为空,则将当前结点入栈,然后将当前结点移动到其左子结点,重复该操作直到当前结点为空。

  2. 将当前结点设为栈顶结点,判断其右子结点是否为空以及是否为上一个访问的结点。

    • 如果当前结点的右子结点不为空且不为上一个访问的结点,则将当前结点移动到其右子结点。

    • 如果当前结点的右子结点为空或者为上一个访问的结点,则将当前结点出栈,将当前结点的结点值加入后序遍历序列,将上一个访问的结点设为当前结点,将当前结点设为空。

  3. 重复上述操作,直到达到遍历的终止条件。

代码

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> traversal = new ArrayList<Integer>();
        Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
        TreeNode node = root, prev = null;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.peek();
            if (node.right != null && node.right != prev) {
                node = node.right;
            } else {
                stack.pop();
                traversal.add(node.val);
                prev = node;
                node = null;
            }
        }
        return traversal;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)

解法三

思路和算法

莫里斯遍历是使用常数空间遍历二叉树的方法,由 J. H. Morris 提出。莫里斯遍历的核心思想是利用二叉树的空闲指针维护遍历顺序,达到省略栈空间的目的。

相比于前序遍历和中序遍历,后序遍历的莫里斯遍历较为复杂。

从根结点开始遍历,遍历的终止条件是当前结点为空。

  1. 对于每个结点,判断当前结点的左子树是否为空,执行相应的操作。

    • 如果当前结点的左子树为空,则将当前结点移动到其右子结点。

    • 如果当前结点的左子树不为空,则找到当前结点的前驱结点,前驱结点为当前结点的左子树中的最右边的结点,判断前驱结点的右子结点是否为空。

      • 如果前驱结点的右子结点为空,则将前驱结点的右子结点设为当前结点,将当前结点移动到其左子结点。

      • 如果前驱结点的右子结点不为空,则将前驱结点的右子结点设为空,将当前结点的左子结点至当前结点的前驱结点的路径上的所有结点的结点值倒序加入后序遍历序列,将当前结点移动到其右子结点。

  2. 重复上述操作,直到达到遍历的终止条件。最后将根结点的结点值加入后序遍历序列。

代码

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> traversal = new ArrayList<Integer>();
        TreeNode node = root;
        while (node != null) {
            if (node.left == null) {
                node = node.right;
            } else {
                TreeNode predecessor = node.left;
                while (predecessor.right != null && predecessor.right != node) {
                    predecessor = predecessor.right;
                }
                if (predecessor.right == null) {
                    predecessor.right = node;
                    node = node.left;
                } else {
                    predecessor.right = null;
                    addToTraversal(node.left, traversal);
                    node = node.right;
                }
            }
        }
        addToTraversal(root, traversal);
        return traversal;
    }

    public void addToTraversal(TreeNode node, List<Integer> traversal) {
        int size1 = traversal.size();
        while (node != null) {
            traversal.add(node.val);
            node = node.right;
        }
        int size2 = traversal.size();
        int left = size1, right = size2 - 1;
        while (left < right) {
            int val1 = traversal.get(left), val2 = traversal.get(right);
            traversal.set(left, val2);
            traversal.set(right, val1);
            left++;
            right--;
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。使用莫里斯遍历,每个结点最多被访问两次。

  • 空间复杂度: O ( 1 ) O(1) O(1)。注意返回值不计入空间复杂度。

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

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

相关文章

沁恒CH32V103 边玩边学1-开发环境与GPIO项目

为什么选择这块板子&#xff1f; 它基于 RISC-V 架构&#xff0c;来看看 GPT 给出的介绍&#xff1a; RISC-V 是一种开源的指令集架构(ISA),与 x86 和 ARM 相似。它具有以下主要特点 • 简单 - RISC-V 采用精炼而简单的 RISC 指令设计,只有一些基本的常用指令。这使得 RISC-V…

CRM未来发展的6大方向

在数字化时代&#xff0c;几乎所有的企业都受到了数字化的洗礼&#xff0c;CRM作为企业数字化转型中的不可缺少的业务系统之一&#xff0c;也受到越来越企业的关注。 纵观CRM发展的趋势&#xff0c;当下CRM系统已经从早期的主要以记录&收集客户资料、管理销售的单点式管理延…

css基础知识四:说说设备像素、css像素、设备独立像素、dpr、ppi 之间的区别?

一、背景 在css中我们通常使用px作为单位&#xff0c;在PC浏览器中css的1个像素都是对应着电脑屏幕的1个物理像素 这会造成一种错觉&#xff0c;我们会认为css中的像素就是设备的物理像素 但实际情况却并非如此&#xff0c;css中的像素只是一个抽象的单位&#xff0c;在不同…

深入浅出Node.js中的node_modules

文章目录 1. 什么是node_modulesnode_modules是什么npm包管理器和node_modules的关系 2. 如何安装和使用node_modulesnpm安装和使用node_modules的基本命令package.json文件的作用和结构npm包版本号的含义及如何管理包版本 3. 如何发布自己的npm包npm包的结构和规范如何将自己的…

端午出行电脑没网怎么办?无线网卡解决网络问题

无线网卡是一种可以让电脑或其他设备通过无线信号连接网络的硬件设备&#xff0c;无线网卡有多种类型和接口&#xff0c;例如USB无线网卡&#xff0c;PCI-E无线网卡&#xff0c;PCMCIA无线网卡等。端午出行在即&#xff0c;不妨看看驱动人生准备的无线网卡攻略&#xff0c;让大…

什么是kafka,如何学习kafka,整合SpringBoot

目录 一、什么是Kafka&#xff0c;如何学习 二、如何整合SpringBoot 三、Kafka的优势 一、什么是Kafka&#xff0c;如何学习 Kafka是一种分布式的消息队列系统&#xff0c;它可以用于处理大量实时数据流。学习Kafka需要掌握如何安装、配置和运行Kafka集群&#xff0c;以及如…

Kubernetes设计架构

一&#xff1a;Kubernetes是什么 Kubernetes是容器集群管理系统&#xff0c;是一个开源的平台&#xff0c;可以实现容器集群的自动化部署、自动扩缩容、维护等功能 通过Kubernetes可以&#xff1a; 快速部署应用 快速扩展应用 无缝对接新的应用功能 节省资源&#xff0c;优化硬…

chatgpt赋能python:Python桌面软件的优势和发展

Python桌面软件的优势和发展 作为一种高级编程语言&#xff0c;Python已经在广大的程序员中得到了越来越广泛的应用&#xff0c;同时也成为了一种非常适合开发桌面软件的语言。下面&#xff0c;我们将重点介绍Python桌面软件的优势和发展。 Python桌面软件的优势 Python编程…

ubuntu18修改源

1. 查看当前系统的源 系统的源 2. 将sources.list备份&#xff0c;sources-bak.list是备份文件 3. 选择要换的源 # 默认注释了源码镜像以提高 apt update 速度&#xff0c;如有需要可自行取消注释 deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted un…

js 获取数组(对象)中的最大和最小值

let arr:array [] // 取最大值&#xff1a;Math.max.apply(Math, arr.map(function(o) {return o.value})) // 取最小值&#xff1a;Math.min.apply(Math, arr.map(function(o) {return o.value}))var array[ { “index_id”: 111, “area_id”: “18335623”, “name”: “满…

Jenkins持续集成构建平台使用指南

目 录 目 录... 2 1、系统参数... 3 2、授权策略... 5 3、构建管理... 6 3.1 构建命名规范... 6 3.1.1 任务视图命名... 6 3.1.2 任务命名... 6 3.2 参数化构建... 7 3.2.1 构建参数列表... 7 3.2.1 常用的参数配置... 8 3.3 分布式构建... 9 3.3.1 slave节点配置..…

基于spss的多元统计分析 之 聚类分析+判别分析(3/8)

实验目的&#xff1a; 1. 掌握多元数据的相关性、正态性、可视化表征的基本原理&#xff1b; 2&#xff0e;熟悉掌握SPSS软件/R软件的基本用法和基本操作&#xff1b; 3&#xff0e;利用实验指导中及软件中内置的实例数据&#xff0c;上机熟悉相关性检验正态性检验可视化数据方…

Kafka如何实现精确一次语义

精确一次交付保证是关于消息传递最具争议性的话题之一&#xff0c;因此也是最复杂的任务之一。然而&#xff0c;几年前&#xff0c;Kafka团队宣布他们实现了这一目标&#xff0c;让我们深入研究一下他们的实现方式以及存在的限制。 首先&#xff0c;值得定义一下这些交付语义是…

CMake在Linux服务器上进行编译与安装

CMake在Linux服务器上进行编译与安装 文章目录 CMake在Linux服务器上进行编译与安装[TOC](文章目录) 一、VScode 远程服务器连接1.服务器容器实例创建2.vscode 远程扩展部分下载&#xff08;SSH端&#xff09; 二、编译安装(时间较长&#xff09;1.克隆项目到本地仓库2.进入CMa…

软考A计划-系统集成项目管理工程师-面向对象系统分析与设计-上

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

spring cloud 5大组件

Spring Cloud 5大组件 服务发现——Netflix Eureka 客服端负载均衡——Netflix Ribbon 断路器——Netflix Hystrix 服务网关——Netflix Zuul 分布式配置——Spring Cloud Config 一、业务场景介绍 先来给大家说一个业务场景&#xff0c;假设咱们现在开发一个电商网站&…

看 AI 如何抢救破烂文档

一、什么是非结构化数据二、非结构化数据分析三、 文档图像分析与预处理 修正图形偏移消除摩尔纹四、消除反光 反光原理Python 消除图片反光方法五、 版面分析与文档还原 5.1 物理版面 & 逻辑版面5.2 版面元素检查5.3 文档还原5.4 文档还原的应用六、整体小结 一、什么是非…

chatgpt赋能python:Python查询网站的SEO技巧及注意事项

Python查询网站的SEO技巧及注意事项 搜索引擎优化&#xff08;SEO&#xff09;是所有网站的头等大事&#xff0c;而对于Python查询网站来说&#xff0c;它更是必不可少的。在这篇文章中&#xff0c;我们将介绍一些Python查询网站的SEO技巧及注意事项&#xff0c;以帮助您提高网…

Elasticsearch分词器

前奏 es的chinese、english、standard等分词器对中文分词十分不友好&#xff0c;几乎都是逐字分词&#xff0c;对英文分词比较友好。 在kibana的dev tools中测试分词&#xff1a; POST /_analyze {"analyzer": "standard","text": "你太…

vue源码分析

1、获取vue源码 项目地址&#xff1a;https://github.com/vuejs/vue 2、文件结构 源码目录 3、调试环境搭建 安装依赖&#xff1a; npm i 安装rollup&#xff1a;npm i -g rollup 修改dev脚本&#xff0c;添加sourcemap&#xff0c;package.json "dev": "ro…