当前位置: 首页 > article >正文

【数据结构】二叉树非递归前中后序遍历详解

二叉树的遍历是二叉树操作的基础核心递归遍历实现简单但存在栈溢出风险在处理深度较大的二叉树时非递归遍历凭借手动维护栈的方式更具稳定性。本文将详细讲解二叉树前序、中序、后序的非递归遍历实现思路结合 C 语言代码完整实现帮助大家理解非递归遍历的核心逻辑。一、二叉树基础准备1.1 二叉树节点结构定义首先定义二叉树的节点结构体包含数据域和左右孩子指针同时定义栈结构用于非递归遍历的节点暂存栈的最大容量通过宏定义MAXSIZE设置。#include stdio.h #include stdlib.h #define MAXSIZE 100 typedef char ElemType; // 二叉树节点结构体 typedef struct TreeNode { ElemType data; // 节点数据字符型 struct TreeNode *lchild; // 左孩子指针 struct TreeNode *rchild; // 右孩子指针 } TreeNode; typedef TreeNode* BiTree; // 栈结构体用于非递归遍历手动维护节点 typedef struct { BiTree data[MAXSIZE]; int top; // 栈顶指针初始为-1表示空栈 } Stack;1.2 二叉树的构建本文所有遍历均基于前序遍历序列构建二叉树序列中用#表示空节点示例序列为ABDH#K###E##CFI###G#J##。构建逻辑为递归读取序列字符非#则创建节点依次递归构建左子树和右子树#则置为空节点。char str[] ABDH#K###E##CFI###G#J##; int idx 0; // 全局索引用于遍历构建序列 // 前序构建二叉树 void createTree(BiTree *T) { ElemType ch str[idx]; if (ch #) { *T NULL; // 空节点 } else { *T (BiTree)malloc(sizeof(TreeNode)); (*T)-data ch; createTree((*T)-lchild); // 先构建左子树 createTree((*T)-rchild); // 后构建右子树 } }1.3 栈的基本操作非递归遍历的核心是手动操作栈实现栈的初始化、判空、入栈、出栈基础功能为后续遍历提供支撑// 初始化栈 void initStack(Stack *s) { s-top -1; } // 判断栈是否为空 int isEmpty(Stack *s) { return s-top -1; } // 入栈成功返回1栈满返回0 int push(Stack *s, BiTree p) { if (s-top MAXSIZE - 1) return 0; s-data[(s-top)] p; return 1; } // 出栈成功返回1栈空返回0p接收出栈节点 int pop(Stack *s, BiTree *p) { if (isEmpty(s)) return 0; *p s-data[(s-top)--]; return 1; }二、非递归前序遍历2.1 遍历规则前序遍历的核心规则是根节点 → 左子树 → 右子树即先访问当前根节点再依次遍历左、右子树。2.2 非递归实现思路若二叉树为空直接返回初始化栈将根节点压入栈中循环处理栈直到栈空弹出栈顶节点立即访问该节点根节点处理先将右孩子压入栈再将左孩子压入栈栈是后进先出保证左孩子先出栈被访问循环结束完成前序遍历。2.3 完整代码实现// 非递归前序遍历 void preOrderNonRecursive(BiTree T) { if (T NULL) return; Stack s; initStack(s); push(s, T); // 根节点入栈 while (!isEmpty(s)) { BiTree p; pop(s, p); // 弹出栈顶节点 printf(%c , p-data); // 访问根节点 // 先压右后压左保证左子树先遍历 if (p-rchild) { push(s, p-rchild); } if (p-lchild) { push(s, p-lchild); } } }2.4 遍历结果基于示例序列构建的二叉树前序非递归遍历结果为A B D H K E C F I G J。三、非递归中序遍历3.1 遍历规则中序遍历的核心规则是左子树 → 根节点 → 右子树即先遍历左子树至最底层再访问根节点最后遍历右子树。3.2 非递归实现思路中序遍历无法像前序那样直接入栈根节点需要先遍历左子树核心思路为初始化栈定义节点指针p指向根节点循环处理条件为p不为空 或 栈不为空若p不为空将p压入栈p指向其左孩子一直向左走遍历左子树若p为空弹出栈顶节点访问该节点左子树遍历完成处理根节点然后p指向其右孩子遍历右子树循环结束完成中序遍历。3.3 完整代码实现// 非递归中序遍历 void inOrderNonRecursive(BiTree T) { Stack s; initStack(s); BiTree p T; while (p ! NULL || !isEmpty(s)) { // 遍历左子树依次入栈 while (p ! NULL) { push(s, p); p p-lchild; } // 左子树遍历完成处理根节点和右子树 if (!isEmpty(s)) { pop(s, p); printf(%c , p-data); // 访问根节点 p p-rchild; // 遍历右子树 } } }3.4 遍历结果基于示例序列构建的二叉树中序非递归遍历结果为H K D B E A I F C G J。四、非递归后序遍历4.1 遍历规则后序遍历的核心规则是左子树 → 右子树 → 根节点是三种遍历中最复杂的因为根节点需要在左右子树都遍历完成后才能访问本文采用双栈法实现逻辑清晰易理解。4.2 双栈法实现思路利用两个栈s1主栈和s2辅助栈通过反转遍历顺序实现后序核心思路若二叉树为空直接返回初始化两个栈将根节点压入s1循环处理s1直到s1为空弹出s1栈顶节点将其压入s2辅助栈暂存先将左孩子压入s1再将右孩子压入s1使s1弹出顺序为根→右→左s2存储顺序为左→右→根的反序循环处理s2依次弹出节点并访问即为左→右→根的后序遍历结果。4.3 完整代码实现// 非递归后序遍历双栈法 void postOrderNonRecursive(BiTree T) { if (T NULL) return; Stack s1, s2; initStack(s1); initStack(s2); push(s1, T); BiTree p; // 主栈s1处理节点按根→右→左压入辅助栈s2 while (!isEmpty(s1)) { pop(s1, p); push(s2, p); // 先压左后压右保证s1弹出右→左 if (p-lchild) { push(s1, p-lchild); } if (p-rchild) { push(s1, p-rchild); } } // 弹出s2得到后序遍历结果 while (!isEmpty(s2)) { pop(s2, p); printf(%c , p-data); } }4.4 遍历结果基于示例序列构建的二叉树后序非递归遍历结果为K H D E B I F J G C A。五、主函数测试将二叉树构建和三种非递归遍历整合到主函数直接运行即可输出遍历结果int main(int argc, char const *argv[]) { BiTree T; createTree(T); // 构建二叉树 printf(非递归前序遍历); preOrderNonRecursive(T); printf(\n非递归中序遍历); inOrderNonRecursive(T); printf(\n非递归后序遍历); postOrderNonRecursive(T); printf(\n); return 0; }完整代码如下#include stdio.h #include stdlib.h // 栈最大容量根据二叉树大小调整 #define MAXSIZE 100 // 定义二叉树节点的数据类型这里用字符型方便演示 typedef char ElemType; // 1. 二叉树节点结构体定义 typedef struct TreeNode { ElemType data; // 节点数据域 struct TreeNode *lchild; // 左孩子指针 struct TreeNode *rchild; // 右孩子指针 } TreeNode; // 给二叉树指针起别名简化代码 typedef TreeNode* BiTree; // 2. 栈结构体定义用于非递归遍历 typedef struct { BiTree data[MAXSIZE]; // 栈存储二叉树节点指针 int top; // 栈顶指针-1表示空栈 } Stack; // 3. 栈的基础操作函数 // 初始化栈 void initStack(Stack *s) { s-top -1; } // 判断栈是否为空空返回1非空返回0 int isEmpty(Stack *s) { return s-top -1; } // 入栈操作将节点p压入栈s int push(Stack *s, BiTree p) { // 栈满判断 if (s-top MAXSIZE - 1) return 0; // 栈顶指针1存入节点 s-data[(s-top)] p; return 1; } // 出栈操作将栈顶节点弹出存入p int pop(Stack *s, BiTree *p) { // 栈空判断 if (isEmpty(s)) return 0; // 取出栈顶节点栈顶指针-1 *p s-data[(s-top)--]; return 1; } // 4. 二叉树创建前序遍历序列创建#表示空节点 // 测试用二叉树序列ABDH#K###E##CFI###G#J## char str[] ABDH#K###E##CFI###G#J##; int idx 0; // 全局索引遍历序列字符串 // 前序递归创建二叉树 void createTree(BiTree *T) { // 读取当前字符 ElemType ch str[idx]; // #代表空节点 if (ch #) { *T NULL; } else { // 分配节点内存 *T (BiTree)malloc(sizeof(TreeNode)); // 赋值节点数据 (*T)-data ch; // 递归创建左子树 createTree((*T)-lchild); // 递归创建右子树 createTree((*T)-rchild); } } // 5. 非递归前序遍历 // 规则根 - 左 - 右 void preOrderNonRecursive(BiTree T) { // 二叉树为空直接返回 if (T NULL) return; Stack s; // 初始化栈 initStack(s); // 根节点先入栈 push(s, T); // 栈不为空循环遍历 while (!isEmpty(s)) { BiTree p; // 弹出栈顶节点 pop(s, p); // 【访问节点】前序先访问根 printf(%c , p-data); // 栈是后进先出所以先压右孩子再压左孩子 // 保证出栈时左孩子先被访问 if (p-rchild) { push(s, p-rchild); } if (p-lchild) { push(s, p-lchild); } } } // 6. 非递归中序遍历 // 规则左 - 根 - 右 void inOrderNonRecursive(BiTree T) { Stack s; initStack(s); // 遍历指针p初始指向根节点 BiTree p T; // 循环条件p不为空 或 栈不为空 while (p ! NULL || !isEmpty(s)) { // 1. 一直向左走把所有左节点入栈直到左节点为空 while (p ! NULL) { push(s, p); p p-lchild; } // 2. 左节点为空弹出栈顶根节点并访问 if (!isEmpty(s)) { pop(s, p); // 【访问节点】中序左走完访问根 printf(%c , p-data); // 3. 访问右子树 p p-rchild; } } } // 7. 非递归后序遍历双栈法最易理解 // 规则左 - 右 - 根 // 双栈法思路s1存节点s2存逆序后序序列最后弹出s2即为后序 void postOrderNonRecursive(BiTree T) { if (T NULL) return; Stack s1, s2; initStack(s1); initStack(s2); // 根节点入s1 push(s1, T); BiTree p; // 处理s1将节点按 根-右-左 压入s2 while (!isEmpty(s1)) { pop(s1, p); push(s2, p); // 先压左后压右保证s1弹出顺序为 根-右-左 if (p-lchild) { push(s1, p-lchild); } if (p-rchild) { push(s1, p-rchild); } } // 弹出s2所有节点就是后序遍历结果 while (!isEmpty(s2)) { pop(s2, p); // 【访问节点】后序左、右都走完访问根 printf(%c , p-data); } } // 8. 主函数测试三种遍历 int main() { BiTree T; // 1. 创建二叉树 createTree(T); // 2. 测试非递归前序遍历 printf(非递归前序遍历结果); preOrderNonRecursive(T); printf(\n); // 3. 测试非递归中序遍历 printf(非递归中序遍历结果); inOrderNonRecursive(T); printf(\n); // 4. 测试非递归后序遍历 printf(非递归后序遍历结果); postOrderNonRecursive(T); printf(\n); return 0; }运行截图如下六、核心总结前序遍历根先出利用栈后进先出先压右、后压左实现根→左→右的访问顺序中序遍历先遍历左子树至最底层左空出根访问再处理右子树循环条件需同时判断节点和栈后序遍历双栈法通过两个栈反转顺序s1 实现根→右→左s2 暂存后弹出即为左→右→根非递归遍历的核心是手动模拟递归的栈帧过程通过栈暂存未处理的节点控制遍历顺序。三种非递归遍历的时间复杂度均为O(n)每个节点入栈、出栈、访问各一次空间复杂度为O(n)最坏情况二叉树退化为链表栈存储所有节点在实际开发中非递归遍历更适合处理深度较大的二叉树避免递归的栈溢出问题。

相关文章:

【数据结构】二叉树非递归前中后序遍历详解

二叉树的遍历是二叉树操作的基础核心,递归遍历实现简单但存在栈溢出风险,在处理深度较大的二叉树时,非递归遍历凭借手动维护栈的方式更具稳定性。本文将详细讲解二叉树前序、中序、后序的非递归遍历实现思路,结合 C 语言代码完整实…...

药流会不会落下月子病?药流后修护要点

药流作为终止早期妊娠的常见方式,其术后养护是否到位,直接关系到女性后续健康,“药流会不会落下月子病”也是行业内及女性群体重点关注的问题。事实上,药流虽无需手术创伤,但对身体的隐性损伤不容忽视,若忽…...

无痛人流三天能出门吗?术后出行与身体恢复科学指南

很多女性在无痛人流术后都会关心出行与恢复问题,其中 “无痛人流三天能出门吗” 是高频咨询内容。术后恢复不仅关系到短期舒适度,也影响生殖系统长期健康。结合临床护理经验与行业康复标准,本文对术后出行时机、注意事项及科学修护方式进行客…...

Pandas 数据分析:统计每个人吃的蔬菜数量

在数据分析中,Pandas 是一个非常强大且灵活的工具,特别是当我们处理数据表格时。今天,我们将通过一个实际例子来展示如何使用 Pandas 统计每个人的蔬菜消费量。这个例子不仅展示了 Pandas 的基本操作,还深入到数据筛选和聚合的细节。 场景描述 假设我们有这样一个 CSV 文…...

Kafka消费者组性能调优实战:从瓶颈识别到极致优化

前言“Kafka性能调优,20%是调整配置,80%是理解你的工作负载。”这是无数生产环境事故总结出来的血泪教训。在生产实践中,很多团队遇到消费性能问题时,第一反应是“加机器、加分区、调参数”,结果往往事倍功半&#xff…...

卡尔曼滤波:详细齐全的代码实现与解析

卡尔曼滤波(代码非常详细、非常齐全) 1、卡尔曼滤波的含义是现时刻的最佳估计为在前一时刻的最佳估计的基础上根据现时刻的观测值作线性修正 2、卡尔曼滤波在数学上是一种线性最小方差统计估算方法,它是通过处理一系列带有误差的实际测量数据…...

基于Simulink的LQR控制四轮转向系统设计与仿真研究

四轮转向 LQR控制 Simulink(个人) 所有算法基于Simulink开发,carsim联合仿真 以期望横摆角速度,零质心侧偏角为状态量,后轮转角为输入,进行离线全速域LQR控制,实现四轮转向,不考虑干…...

果园灌溉施肥控制系统升级:博图v16西门子s7-1200PLC选型与运行效果展示

果园灌溉施肥控制系统改3 博图v16,西门子s7-1200PLC带选型表 io表 运行效果视频果园灌溉3.0版本升级用上了博图V16和西门子S7-1200 PLC,这次改造最大的亮点是把施肥和滴灌控制集成到了同一个系统里。先说个实战经验:在新疆某果园调试时&…...

论文降重降AI难?自带双功能黑科技的实用工具盘点

论文降重和消除AI生成痕迹是很多创作者面临的双重难题,选对工具能节省大量时间精力。下面整理了几款自带降AIGC率功能的实用工具,覆盖中文、英文、应急、轻量优化等不同使用场景,附实际使用效果与核心特点,帮你快速找到适配需求的…...

降AI率低至2%:SpeedAI科研小助手,论文过审省心利器

很多同学都在找能稳定过AIGC检测的工具,其实从 99.8% 到 14.9%:Paperxie AI 降重,破解论文 AIGC 检测的终极方案-CSDN博客这类分享里提到的核心需求,SpeedAI科研小助手都能更好地满足。一、写在前面:被AIGC检测支配的论…...

论文AI率太高怎么降?去AI化实用技巧与工具避坑指南

“整篇论文都是自己原创的,就用AI顺了下逻辑,结果学校AIGC检测直接飙到73%,当场被打回”“熬了3个通宵手动改,AI率才降了不到12%,离答辩只剩一周根本赶不完”“随便找了个降AI工具,把我专业名词改得乱七八糟…...

论文写作卡壳不用愁!这几款AI工具帮你高效赶稿

写论文最怕思路卡壳?大纲憋不出来、正文续写没头绪、降重改到崩溃,还担心AI生成痕迹过不了检测?以下几款实用AI写作工具直击本硕生核心需求,从初稿到答辩全流程辅助,让写作效率直接翻倍。 一、SpeedAI科研小助手&#…...

SEO_如何通过内容SEO获取稳定流量的关键方法

SEO:如何通过内容SEO获取稳定流量的关键方法 在当今数字化时代,如何通过内容SEO获取稳定流量成为了许多企业和网站运营者关注的焦点。内容SEO不仅能够提升网站的自然搜索排名,还能为网站带来长期的、可持续的流量。具体应该如何通过内容SEO获取稳定流量…...

学术效率倍增:Zotero插件全生命周期管理的创新实践

学术效率倍增:Zotero插件全生命周期管理的创新实践 【免费下载链接】zotero-addons Zotero Add-on Market | Zotero插件市场 | Browsing, installing, and reviewing plugins within Zotero 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-addons 一、…...

实测nanobot:5分钟搭建个人AI助手,还能轻松接入QQ聊天

实测nanobot:5分钟搭建个人AI助手,还能轻松接入QQ聊天 1. nanobot核心优势解析 nanobot作为一款超轻量级个人AI助手解决方案,在技术架构和用户体验上都有显著突破。这个受OpenClaw启发的项目,仅用约4000行代码就实现了完整的智能…...

新手必看:虚拟机安装SQL Server全攻略

对于初学者来说 我们并不能使用现实的物理环境来进行练手sql服务 那么就需要使用虚拟环境安装sql sever服务 这样的好处是 不仅可以得到真实物理环境的练手 还可以发现任何问题得到还原和解决 那么就看看如何在虚拟环境下安装sql 服务吧一、准备工作1、虚拟机准备本次使用的是v…...

Elsevier投稿状态监控插件:3分钟告别手动刷新的终极解决方案

Elsevier投稿状态监控插件:3分钟告别手动刷新的终极解决方案 【免费下载链接】Elsevier-Tracker 项目地址: https://gitcode.com/gh_mirrors/el/Elsevier-Tracker 你是否每天都要反复登录Elsevier投稿系统,只为查看那迟迟不来的审稿状态&#xf…...

LLM性能评估入门到精通,搞懂推理指标看这篇就够了!

TTFT、TPOT、ITL、Goodput… 这些指标到底什么意思?今天用一篇文章彻底讲清楚 LLM 推理的性能评估体系。 一、为什么指标很重要 生产环境的真实场景 你部署了一个大模型服务,用户反馈: “首字响应好慢” → 什么问题?“生成过程…...

基于深度学习的车牌识别系统(YOLO12/11/v8/v5模型+django)(源码+lw+部署文档+讲解等)

摘要随着智能交通系统的迅猛发展,车牌识别技术在交通管理、停车场管理和公共安全等领域的应用愈加广泛。传统的车牌识别方法多依赖于图像处理技术,无法有效应对复杂环境下的车牌识别任务。为了解决这一问题,本文提出了一种基于深度学习的车牌…...

openclaw连接飞书操作表格

01意义 将智能助手从电脑网页端连接到手机飞书,从此无需守在电脑前,用手机就能随时指挥它干活。未来,飞书中需要手动操作的任务,都可以交由 AI 智能助手来完成。它还能帮你构建企业知识库,随着飞书终端 CLI 能力的增强…...

基于深度学习的田间杂草检测系统(YOLOv12/v11/v8/v5模型)(源码+lw+部署文档+讲解等)

摘要田间杂草的生长不仅会影响作物的产量和质量,还会对农田管理造成巨大挑战。传统的杂草检测方法多依赖人工观察,效率低下且受主观因素影响。为了提高田间杂草的检测效率与准确性,本文提出了一种基于深度学习的田间杂草检测系统,…...

基于深度学习的隧道缺陷检测系统(YOLO12/11/v8/v5模型+django)(源码+lw+部署文档+讲解等)

摘要随着城市化进程的加快,隧道的建设和维护日益重要。隧道缺陷的及时检测与修复不仅关系到交通安全,也涉及到基础设施的耐久性和经济效益。传统的隧道缺陷检测方法依赖人工巡检,效率低且容易遗漏细微缺陷。本文提出了一种基于深度学习的隧道…...

基于深度学习的水下海洋生物识别(YOLOv12/v11/v8/v5模型+数据集)(源码+lw+部署文档+讲解等)

摘要随着全球海洋生态环境的变化,水下海洋生物的监测与识别变得日益重要。基于深度学习的水下生物识别技术,尤其是YOLO(You Only Look Once)系列目标检测模型,因其高效性和准确性而受到广泛关注。本文提出了一种基于YO…...

**发散创新:基于同态加密的隐私保护计算在Python中的实战实现**随

发散创新:基于同态加密的隐私保护计算在Python中的实战实现 随着数据安全需求的不断升级,同态加密(Homomorphic Encryption) 正从理论走向落地。它允许对加密数据直接进行计算,结果解密后与明文计算一致——这为云计算…...

杰理之SDK 增加通话翻译(OPUS 立体声)功能【篇】

AI 翻译功能...

AI Agent 与传统AI区别:从被动响应到主动执行

AI Agent 与传统AI区别:从被动响应到主动执行📝 本章学习目标:本章是入门认知部分,帮助零基础读者建立对AI Agent的初步认知。通过本章学习,你将全面掌握"AI Agent 与传统AI区别:从被动响应到主动执行…...

计算机毕业设计:Python汽车销量大数据预测平台 Flask框架 可视化 机器学习 AI 大模型 大数据(建议收藏)✅

博主介绍:✌全网粉丝50W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战8年之久,选择我们就是选择放心、选择安心毕业✌ > 🍅想要获取完整文章或者源码,或者代做,拉到文章底部即可与…...

计算机毕业设计:Python智能汽车销量分析预测平台 Flask框架 scikit-learn 可视化 requests爬虫 AI 大模型(建议收藏)✅

博主介绍:✌全网粉丝50W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战8年之久,选择我们就是选择放心、选择安心毕业✌ > 🍅想要获取完整文章或者源码,或者代做,拉到文章底部即可与…...

ViGEmBus终极指南:3分钟掌握Windows虚拟游戏手柄驱动

ViGEmBus终极指南:3分钟掌握Windows虚拟游戏手柄驱动 【免费下载链接】ViGEmBus Windows kernel-mode driver emulating well-known USB game controllers. 项目地址: https://gitcode.com/gh_mirrors/vi/ViGEmBus ViGEmBus是一款强大的Windows内核级驱动程序…...

北京交通大学 | 基于TD3算法的层叠超表面辅助多用户MISO系统联合优化研究

引言随着无线通信技术的不断发展,可重构智能表面(RIS)技术因其低功耗和信号操控能力而受到广泛关注。然而,RIS的单层结构和离散相移能力限制了其性能表现。层叠智能超表面(SIM)作为一项创新技术&#xff0c…...