二叉树—中序遍历—非递归

 

初始状态

假设当前从根节点 b 开始,此时栈为空 。

第一步:处理根节点 b 的左子树

  • 调用 goAlongLeftBranch 函数,从节点 b 开始,因为 b 有左子树(节点 a ),将 b 入栈,此时栈:[b] ;继续沿着左分支,a 没有左子树了,将 a 入栈 ,此时栈:[a, b] 。
  • 回到主循环,栈非空,执行 x = S.pop() ,a 出栈并访问 a ;然后 x 指向 a 的右子树,a 右子树为空,回到主循环继续判断栈状态。

第二步:处理根节点 b

  • 此时栈顶是 b ,执行 x = S.pop() ,b 出栈并访问 b ;然后 x 指向 b 的右子树(节点 f )。

第三步:处理 b 右子树(以 f 为根的子树 )

  • 调用 goAlongLeftBranch 函数,从节点 f 开始,因为 f 有左子树(节点 d ),将 f 入栈,此时栈:[f] ;继续沿着左分支,d 有左子树(节点 c ),将 d 入栈 ,此时栈:[d, f] ;c 没有左子树了,将 c 入栈 ,此时栈:[c, d, f] 。
  • 回到主循环,栈非空,执行 x = S.pop() ,c 出栈并访问 c ;然后 x 指向 c 的右子树,c 右子树为空,回到主循环继续判断栈状态。

第四步:继续处理 f 左子树剩余部分

  • 此时栈顶是 d ,执行 x = S.pop() ,d 出栈并访问 d ;然后 x 指向 d 的右子树(节点 e )。
  • 将 e 入栈,此时栈:[e, f] ,执行 x = S.pop() ,e 出栈并访问 e ;e 右子树为空,回到主循环继续判断栈状态。

第五步:处理根节点 f

  • 此时栈顶是 f ,执行 x = S.pop() ,f 出栈并访问 f ;然后 x 指向 f 的右子树(节点 g )。
  • 将 g 入栈,此时栈:[g] ,执行 x = S.pop() ,g 出栈并访问 g ;g 右子树为空,此时栈为空,满足 S.empty() 条件,break 跳出循环,中序遍历结束。

 

 

时间复杂度的证明:

 

简要分析:

关键操作包括:入栈( goAlongLeftBranch 函数)和出栈节点访问(主循环)

入栈 

goAlongLeftBranch 函数:沿着二叉树的左分支不断将节点入栈。最坏情况下,二叉树退化为一条单链(eg:所有节点只有左孩子 ),此时对于一个具有 n 个节点的二叉树,调用 goAlongLeftBranch 函数时  需将 n 个节点依次入栈,每次入栈操作可视为 O(1) ,所以单次 goAlongLeftBranch 调用  在最坏情况下的时间复杂度为 Ω(n) 。

出栈及节点访问

主循环中,每次迭代会执行一次出栈并访问出栈节点。每个节点只会入栈一次且出栈一次(每个节点在沿着左分支时入栈,出栈访问后不会再次入栈 )。出栈和访问节点都是O(1) 。由于二叉树共有 n 个节点,所以出栈及节点访问操作的总时间复杂度为 O(n) 。

分摊

虽然单次 goAlongLeftBranch 调用可能花费 Ω(n) 时间(如二叉树为单链时 ),但从整个遍历过程来看,每个节点入栈和出栈操作总共最多各一次。

例如,当沿着左分支集中进行多次入栈(耗时较长 )时,后续这些入栈节点会依次出栈并被访问。把前面集中入栈的较长时间开销分摊到后续这些节点出栈及访问的过程中。从整体 n 个节点的处理来看,平均每个节点的操作时间是常数级别的。所以整个中序遍历算法的时间复杂度为 O(n) 。

 

具体分析:

template <typename T>
static void goAlongLeftBranch( BinNodePosi(T) x, Stack <BinNodePosi(T)> &S ) {while ( x ) { S.push( x ); x = x->lc; } // 反复地入栈,沿左分支深入
}

作用:沿当前节点 x 的左分支不断将节点入栈。每次循环执行 S.push(x)(入栈,时间复杂度为 O(1))和 x=x−>lc(指针移动,时间复杂度为 O(1))。

对于每个节点,它只会从左分支方向被访问一次并入栈。整个二叉树共有 n 个节点,因此所有节点入栈的总次数为 n,总时间复杂度为 O(n)。

 

template <typename T, typename V> 
void travIn_I1( BinNodePosi(T) x, V& visit ) { Stack <BinNodePosi(T) > S; // 辅助栈while ( true ) { // 反复地goAlongLeftBranch( x, S ); // 从当前节点出发,逐批入栈if ( S.empty() ) break; // 直至所有节点处理完毕x = S.pop(); // x的左子树或为空,或已遍历(等效于空),故可以visit( x->data ); // 立即访问之x = x->rc; // 再转向其右子树(可能为空,留意处理手法)}
}

 

出栈x = S.pop() 为出栈,时间复杂度为 O(1)。每个节点入栈后仅出栈一次,总出栈次数为 n,总时间复杂度为 O(n)。

访问节点visit( x->data ) 用于访问节点数据,每个节点仅被访问一次,时间复杂度为 O(1),n 个节点总时间复杂度为 O(n)。

转向右子树x = x->rc 是指针移动,时间复杂度为 O(1)。每个节点处理完后转向右子树(即使右子树为空也会执行一次该操作),总操作次数为 n,总时间复杂度为 O(n)。

虽然 goAlongLeftBranch 函数单次调用可能沿长左链做多次入栈(如树退化为左单链时),但从全局看,每个节点的入栈、出栈、访问及转向右子树操作均为 O(1) 且仅执行一次。

总时间复杂度为各部分时间复杂度之和:O(n)+O(n)+O(n)+O(n)=O(n)。

此二叉树中序遍历代码的时间复杂度为 O(n),每个节点都被且仅被处理一次,所有操作的总耗时与节点数 n 呈线性关系。

 

 

1.入栈

goAlongLeftBranch 函数中 while (x) { S.push(x); x = x->lc; } 会沿左分支将节点入栈。每个节点仅入栈一次,总入栈次数为 n(n 为节点总数),入栈总时间为 O(n)。

2. 外层循环

  • 出栈x = S.pop() 每个节点仅出栈一次,总出栈次数为 n,时间为 O(n)。
  • 访问节点visit(x->data) 每个节点仅访问一次,时间为 O(n)。
  • 转向右子树x = x->rc 每个节点处理后转向右子树(即使为空也执行一次),总操作次数为 n,时间为 O(n)。

虽然有循环嵌套,但每个节点的入栈、出栈、访问、转向右子树操作均为 O(1) 且仅执行一次。所有操作的总时间复杂度为各部分之和:

O(n)(入栈)+O(n)(出栈)+O(n)(访问)+O(n)(转向右子树)=O(n)


外层循环,每次出栈一个节点,处理它的右子树。每个节点出栈一次,访问一次。虽然外层是 while (true),但每次循环都处理一个节点,并且每个节点的入栈和出栈都是一次。

时间复杂度看的是每个操作的总次数。入栈总次数是 n,出栈总次数是 n,每个节点处理一次。所以总的时间复杂度是 O (n)

一句话:循环嵌套看似为n^2,但在while(true)中每个节点只被处理一次,没有重复处理,所以是 O (n)

DSACPP

 

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

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

相关文章

R 语言科研绘图第 45 期 --- 桑基图-和弦

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.weixin.qq.c…

ARM 流控制指令

计算机按照严格的顺序执行指令。流控制改变了默认的顺序执行方式。前面已 经介绍了强制跳转到程序中某个非顺序位置的无条件分支。以及依据测试结果 进行跳转的条件分支。这里将介绍子程序调用和返回指令&#xff0c;它们会跳转到一个 指令块、执行这些指令&#xff0c;然后返回…

PDF内容搜索--支持跨文件夹多文件、组合词搜索

平时我们接触到的PDF文档特别多&#xff0c;需要对PDF文档做一些处理&#xff0c;那么今天给大家带来的这两个软件非常的棒&#xff0c;可以帮你提升处理文档的效率。 PDF内容搜索 快速检索 我用夸克网盘分享了「PDF搜索PDF 转长图.zip」&#xff0c;点击链接即可保存。打开「…

个人Unity自用面经(未完)

目录标题 1.在 2D 平台跳跃游戏项目中&#xff0c;你使用了对象池来生成和回收怪物包含阵亡的动画预制件。在对象池回收对象时&#xff0c;如何确保动画状态被正确重置&#xff0c;避免下次使用时出现异常&#xff1f;2.在僵尸吃脑子模拟项目中&#xff0c;你创建了继承于IAspe…

【计网】ICMP、IP、Ethernet PDU之间的封装关系

TCP/IP体系结构 应用层RIP、OSPF、FTP运输层TCP、UDP网际层IP、ARP、ICMP网络接口层底层协议&#xff08;Ethernet&#xff09; 数据链路层 Ethernet报文格式 6Byte6Byte2Byte46~1500Byte4Byte目的MAC地址源MAC地址类型/长度数据FCS 其中&#xff0c;类型 / 长度值小于 1536…

前端取经路——入门取经:初出师门的九个CSS修行

大家好&#xff0c;我是老十三&#xff0c;一名前端开发工程师。CSS就像前端修行路上的第一道关卡&#xff0c;看似简单&#xff0c;实则暗藏玄机。在今天的文章中&#xff0c;我将带你一起应对九大CSS难题&#xff0c;从Flexbox布局到响应式设计&#xff0c;从选择器优先级到B…

n8n工作流自动化平台的实操:Cannot find module ‘iconv-lite‘

解决问题&#xff1a; 1.在可视化界面&#xff0c;执行const iconv require(iconv-lite);&#xff0c;报Cannot find module iconv-lite [line 2]错误&#xff1b; 查看module的路径 进入docker容器 #docker exec -it n8n /bin/sh 构建一个test.js,并写入如何代码 vi tes…

如何在 PowerEdge 服务器上设置 NIC 分组

以下文章提供了有关 Windows、VMware 和 Linux 中的 NIC 分组的信息。 什么是网络适配器分组&#xff1f;设置 NIC 分组 Windows设置 NIC 分组 VMware设置 NIC 分组 Linux 什么是网络适配器分组&#xff08;绑定&#xff09;&#xff1f; 网络适配器分组是一个术语&#xff0…

【Java ee初阶】多线程(5)

一、wait 和 notify wait notify 是两个用来协调线程执行顺序的关键字&#xff0c;用来避免“线程饿死”的情况。 wait 和 notify 其实都是 Object 这个类的方法&#xff0c;而 Object这个类是所有类的“祖宗类”&#xff0c;也就是说明&#xff0c;任何一个类&#xff0c;都…

基于k8s的Jenkins CI/CD平台部署实践(二):流水线构建与自动部署全流程

基于k8s的Jenkins CI/CD平台部署实践&#xff08;二&#xff09;&#xff1a;流水线构建与自动部署全流程 文章目录 基于k8s的Jenkins CI/CD平台部署实践&#xff08;二&#xff09;&#xff1a;流水线构建与自动部署全流程一、Jenkins简介二、系统架构与环境说明1. 系统架构2.…

《Windows 环境下 Qt C++ 项目升级 GCC 版本的完整指南》

Windows 环境下 Qt C++ 项目升级 GCC 版本的完整指南 在 Windows 系统中升级 Qt C++ 项目的 GCC 版本需要同时考虑 Qt 工具链、MinGW 环境以及项目配置的调整。以下是详细的升级步骤和注意事项: 一、升级前的准备工作 1. 确认当前环境 检查 Qt 版本(建议使用 Qt 5.15+ 以获…

【coze】故事卡片(图片、音频、文字)

【coze】故事卡片&#xff08;图片、音频、文字&#xff09; 1、创建智能体2、添加人设与回复逻辑3、添加工作流&#xff08;1&#xff09;创建工作流&#xff08;2&#xff09;添加大模型节点&#xff08;3&#xff09;添加提示词优化节点&#xff08;4&#xff09;添加豆包图…

Maven 依赖发布与仓库治理

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

虚拟现实视频播放器 2.6.1 | 支持多种VR格式,提供沉浸式观看体验的媒体播放器

虚拟现实媒体播放器是一款专为在智能手机上播放VR&#xff08;虚拟现实&#xff09;照片和视频而设计的应用程序。它支持多种格式的影像内容&#xff0c;包括360和180等距矩形柱面、标准镜头和鱼眼镜头拍摄的照片和视频&#xff0c;并且兼容3D立体并排、上/下以及收缩媒体格式。…

ts axios中报 Property ‘code‘ does not exist on type ‘AxiosResponse<any, any>‘

ts语法有严格的格式&#xff0c;如果我们在处理响应数据时&#xff0c;出现了axios响应中非默认字段&#xff0c;就会出现标题那样的警告&#xff0c;我们可以通过创建axios.dt.ts解决这个问题 下面是我在开发中遇到的警告&#xff0c;code并不是axios默认返回的字段&#xff0…

tinyrenderer笔记(Shadow Mapping)

tinyrenderer个人代码仓库&#xff1a;tinyrenderer个人练习代码 前言 阴影是光线被阻挡的结果&#xff1b;当光源的光线由于其他物体的阻挡而无法到达物体表面时&#xff0c;该物体就会产生阴影。阴影能使场景看起来更真实&#xff0c;并让观察者获得物体之间的空间位置关系。…

debian中笔记本的省电选择auto-cpufreq

在reddit中&#xff0c;看评论区出现这个软件&#xff0c;于是打算尝试一下&#xff0c;应该能对不使用电源时笔记本的省电起到一定的作用。 https://github.com/AdnanHodzic/auto-cpufreq?tabreadme-ov-file#why-do-i-need-auto-cpufreq 作用 One of the problems with Linux…

Windows 查看电脑是否插拔过U盘

1、按 “WinR” 组合键打开 “运行” 对话框&#xff0c;输入 “regedit” 并回车&#xff0c;打开注册表编辑器。 2、依次展开HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR注册表项&#xff0c;这里记录了所有已连接过的 USB 设备信息&#xff0c;包括 U 盘&am…

426、N叉树的层序遍历

输入检查&#xff1a; if not root:return [] 如果根节点为空&#xff0c;直接返回空列表 初始化&#xff1a; result [] queue collections.deque([root]) result用于存储最终结果queue初始化包含根节点&#xff0c;使用双端队列实现 主循环&#xff1a; while queue:leve…

【ES】Elasticsearch字段映射冲突问题分析与解决

在使用Elasticsearch作为搜索引擎时&#xff0c;经常会遇到一些映射(Mapping)相关的问题。本文将深入分析字段映射冲突问题&#xff0c;并通过原生的Elasticsearch API请求来复现和解决这个问题。 问题描述 在实际项目中&#xff0c;我们遇到以下错误&#xff1a; Transport…