94. 二叉树的中序遍历(Swift实现, 迭代)

题目描述

在这里插入图片描述

使用迭代方法解题

class TreeNode {
    var val: Int
    var left: TreeNode?
    var right: TreeNode?
    
    init(_ val: Int) {
        self.val = val
        self.left = nil
        self.right = nil
    }
}

func inorderTraversal(_ root: TreeNode?) -> [Int] {
    var result = [Int]()    // 用于存储中序遍历的结果
    var stack = [TreeNode]() // 用于模拟递归的栈
    var current = root       // 从根节点开始

    // 当当前节点不为空或栈不为空时继续循环
    while current != nil || !stack.isEmpty {
        // 不断将当前节点的左子节点压入栈中,直到当前节点为空
        while let node = current {
            stack.append(node)
            current = node.left
        }

        // 弹出栈顶节点,并将其值添加到结果数组
        current = stack.removeLast()
        result.append(current.val)
        
        // 将当前节点指向弹出节点的右子节点
        current = current.right
    }
    
    return result
}

// 示例用法:
// 构建一个示例二叉树
//      1
//     / \
//    2   3
//   / \
//  4   5

let root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left?.left = TreeNode(4)
root.left?.right = TreeNode(5)

// 调用中序遍历函数
let result = inorderTraversal(root)
print(result)  // 输出: [4, 2, 5, 1, 3]

分析

理解迭代方式的二叉树中序遍历确实需要一些时间,因为它涉及到手动管理一个栈来模拟递归过程。

手动管理一个栈来模拟递归过程,主要在于理解递归调用的本质:保存当前的执行状态,然后在处理完子任务后恢复这个状态。以下是一些窍门和技巧,帮助你更好地理解和实现这种方式:

理解递归和迭代的等效性

  1. 递归调用栈

    • 每次递归调用,系统会把当前函数的状态(变量、执行位置等)压入系统栈。
    • 当子任务完成后,系统会从栈中恢复上一次的状态继续执行。
  2. 迭代模拟递归

    • 我们需要手动管理一个栈,把当前节点和相关信息压入栈中。
    • 在处理子任务时,可以从栈中恢复之前的状态。

栈操作的核心逻辑

中序遍历的递归方式:

func inorderTraversal(_ root: TreeNode?) -> [Int] {
    guard let root = root else { return [] }
    return inorderTraversal(root.left) + [root.val] + inorderTraversal(root.right)
}

转换为迭代方式:

步骤详细解析

  1. 初始化

    • 使用一个栈来存储节点。
    • 使用一个变量 current 来追踪当前节点。
  2. 模拟递归的左子树处理

    • 不断将当前节点压入栈,并移向左子节点,直到当前节点为空。
    • 这一步类似递归调用到最左子节点的过程。
  3. 处理节点并移向右子树

    • 弹出栈顶节点,处理该节点(例如添加到结果数组中)。
    • current 指向该节点的右子节点。

具体代码

class TreeNode {
    var val: Int
    var left: TreeNode?
    var right: TreeNode?
    
    init(_ val: Int) {
        self.val = val
        self.left = nil
        self.right = nil
    }
}

func inorderTraversal(_ root: TreeNode?) -> [Int] {
    var result = [Int]()        // 存储中序遍历结果
    var stack = [TreeNode]()    // 栈用于模拟递归
    var current = root          // 从根节点开始遍历

    // 当栈不为空或当前节点不为空时循环继续
    while current != nil || !stack.isEmpty {
        // 一直向左走,直到没有左子节点
        while let node = current {
            stack.append(node)  // 将当前节点压入栈
            current = node.left // 移动到左子节点
        }

        // 弹出栈顶节点并处理
        current = stack.removeLast() // 弹出栈顶
        result.append(current.val)   // 处理当前节点
        
        // 转向右子树
        current = current.right      // 移动到右子节点
    }
    
    return result
}

// 示例用法:
// 构建一个示例二叉树
//      1
//     / \
//    2   3
//   / \
//  4   5

let root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left?.left = TreeNode(4)
root.left?.right = TreeNode(5)

// 调用中序遍历函数
let result = inorderTraversal(root)
print(result)  // 输出: [4, 2, 5, 1, 3]

关键点总结

  1. 模拟递归压栈

    • 每次将当前节点压入栈中,然后移动到左子节点。
    • 这相当于递归调用处理左子树。
  2. 模拟递归出栈

    • 当无法继续向左走时,弹出栈顶节点。
    • 处理该节点后,转向右子树。
  3. 继续遍历

    • current 指向右子节点,继续上述过程。

理解示例

对于二叉树:

     1
    / \
   2   3
  / \
 4   5
  • 初始状态
    • current 指向 1,stack 为空。
  • 第一次内循环(处理左子树):
    • 压入 1 -> 压入 2 -> 压入 4 -> 到达 nil
  • 第一次弹出
    • 弹出 4,添加到结果:result = [4]current 指向 nil
  • 第二次弹出
    • 弹出 2,添加到结果:result = [4, 2]current 指向 5。
  • 处理 5
    • 压入 5 -> 到达 nil -> 弹出 5,添加到结果:result = [4, 2, 5]
  • 处理 1
    • 弹出 1,添加到结果:result = [4, 2, 5, 1]current 指向 3。
  • 处理 3
    • 压入 3 -> 到达 nil -> 弹出 3,添加到结果:result = [4, 2, 5, 1, 3]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[深度学习]基于C++和onnxruntime部署yolov10的onnx模型

基于C和ONNX Runtime部署YOLOv10的ONNX模型,可以遵循以下步骤: 准备环境:首先,确保已经下载后指定版本opencv和onnruntime的C库。 模型转换:按照官方源码:https://github.com/THU-MIG/yolov10 安装好yolov…

【OpenVINO™】使用 OpenVINO™ C++ 异步推理接口部署YOLOv8 ——在Intel IGPU 上实现80+FPS视频推理

​ OpenVINO Runtime支持同步或异步模式下的推理。Async API的主要优点是,当设备忙于推理时,应用程序可以并行执行其他任务(例如,填充输入或调度其他请求),而不是等待当前推理首先完成。 当我们使用异步API…

图片查看器

目录 一 原型 二 源码 一 原型 二 源码 namespace 图片查看器 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){//默认显示第一张图片pictureBox1.Image imageList1.Images[0];}private v…

13. 第十三章 案例研究-选择数据结构

13. 案例研究-选择数据结构 到这里尼应该已经学会了Python的核心数据结构, 也见过了一些使用它们的算法. 如果你想要更多地了解算个发可以阅读第21章. 本章配合联系介绍一个案例分析, 帮你思考如何选择数据结构并如何使用它们.13.1 单词频率分析 1. 练习1 编写一个程序, 读入…

《Brave New Words 》9.1 AI 世界中的就业

Part IX: Work and What Comes Next 第九部分:工作及其未来发展 The one who plants trees, knowing that he will never sit in their shade, has at least started to understand the meaning of life. —Rabindranath Tagore 种树的人,虽然知道他永远…

上位机图像处理和嵌入式模块部署(h750 mcu串口命令处理)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面学习103和407的时候,当时学过串口的收发。不过当时使用的主要是阻塞的方式。这一次,我们看下应该怎么利用中断的形式进…

基于flask的网站如何使用https加密通信

文章目录 内容简介网站目录示例生成SSL证书单独使用Flask使用WSGI服务器Nginx反向代理参考资料 内容简介 HTTPS 是一种至关重要的网络安全协议,它通过在 HTTP 协议之上添加 SSL/TLS 层来确保数据传输的安全性和完整性。这有助于防止数据在客户端和服务器之间传输时…

前端实现获取后端返回的文件流并下载

前端实现获取后端返回的文件流并下载 方法一:使用Axios实现文件流下载优点缺点 方法二:使用封装的Request工具实现文件流下载优点缺点 方法三:直接通过URL跳转下载优点缺点 结论 在前端开发中,有时需要从后端获取文件流&#xff0…

Android studio如何导入项目

打开解压好的安装包 找到build.gradle文件 打开查看gradle版本 下载对应的gradle版本Index of /gradle/(镜像网站) 下载all的对应压缩包 配置gradle的环境变量 新建GRADLE_HOME 将GRADLE_HOME加入到path中 将项目在Android studio中打开进行配置 将gr…

前端 CSS 经典:在 Vue3 中使用渐进式图片

1. 什么是渐进式图片 当我们网站会加载很多图片的时候,有些图片尺寸很大,加载就会很慢,会导致页面长时间陷入白屏状态,用户体验很不好。所以可以使用渐进式图片,先给用户展示模糊图,这些图尺寸小&#xff…

嵌入式硬件VS软件,到底哪个更难?

在嵌入式系统开发中,硬件和软件是密不可分的两个方面。但是,究竟是硬件开发更具挑战性,还是软件开发更难以应对呢?本文将就这一问题展开讨论,探究嵌入式硬件和软件在开发过程中的各种挑战与特点。 一、硬件开发&#…

5.7 Python内置函数

文章目录 1. 内置模块Aabs()all()any()ascii() Bbin()bool()bytearra()bytes() Ccallable()chr()classmethod()compile()complex() Ddelattr()dict()dir()divmod() Eenumerate()eval()exec()execfile() Ffile()filter()float()format()frozenset() Ggetattr()globals() Hhasatt…

C++ 23 之 构造函数和析构函数

c23构造函数和析构函数.cpp #include <iostream> #include <string> using namespace std;class Person2{ public:// 构造函数 没有返回值&#xff0c;不能写void;函数名和类名一致&#xff1b;可以设置参数&#xff0c;可以函数重载&#xff1b;系统自动调用&…

人工智能将成为数学家的“副驾驶”

人工智能将成为数学家的“副驾驶” 数学传统上是一门独立的科学。1986年&#xff0c;安德鲁怀尔斯为了证明费马定理&#xff0c;退到书房里呆了7年。由此产生的证明往往很难让同事们理解&#xff0c;有些至今仍有争议。但近年来&#xff0c;越来越多的数学领域被严格地分解为各…

[大模型]Phi-3-mini-4k-instruct langchain 接入

环境准备 在 autodl 平台中租赁一个 3090 等 24G 显存的显卡机器&#xff0c;如下图所示镜像选择 PyTorch–>2.0.0–>3.8(ubuntu20.04)–>11.8 。 接下来打开刚刚租用服务器的 JupyterLab&#xff0c;并且打开其中的终端开始环境配置、模型下载和运行演示。 创建工作…

C++ | Leetcode C++题解之第150题逆波兰表达式求值

题目&#xff1a; 题解&#xff1a; class Solution { public:int evalRPN(vector<string>& tokens) {int n tokens.size();vector<int> stk((n 1) / 2);int index -1;for (int i 0; i < n; i) {string& token tokens[i];if (token.length() >…

Siemens-NXUG二次开发-创建平面(无界非关联)、固定基准面[Python UF][20240614]

Siemens-NXUG二次开发-创建平面&#xff08;无界非关联&#xff09;、固定基准面[Python UF][20240614] 1.python uf函数1.1 NXOpen.UF.Modeling.CreatePlane1.2 NXOpen.UF.ModlFeatures.CreateFixedDplane 2.示例代码2.1 pyuf_plane.py 3.运行结果3.1 内部模式3.1.1 NXOpen.UF…

DTU为何应用如此广泛?

1.DTU是什么 DTU(数据传输单元)是一种无线终端设备&#xff0c;它的核心功能是将串口数据转换为IP数据或将IP数据转换为串口数据&#xff0c;并通过无线通信网络进行传送。DTU通常内置GPRS模块&#xff0c;能够实现远程数据的实时传输&#xff0c;广泛应用于工业自动化、远程监…

【MySQL】日志详解

本文使用的MySQL版本是8 日志概览 它们记录了数据库系统中的不同操作和事件&#xff0c;以便于故障排除、性能优化和数据恢复。本文将介绍MySQL中常见的几种日志&#xff0c;同时也会介绍一点常用的选项。 官方文档&#xff1a;MySQL :: MySQL 8.0 Reference Manual :: 7.4 M…

Golang | Leetcode Golang题解之第149题直线上最多的点数

题目&#xff1a; 题解&#xff1a; func maxPoints(points [][]int) (ans int) {n : len(points)if n < 2 {return n}for i, p : range points {if ans > n-i || ans > n/2 {break}cnt : map[int]int{}for _, q : range points[i1:] {x, y : p[0]-q[0], p[1]-q[1]if…