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

数据结构 - 二叉树的非递归方式遍历

在实现二叉树遍历时,使用递归的方式是比较直观的方式。然而递归依赖系统调用栈,一方面在树深度很大时可能导致栈溢出,另一方面在工程实践中往往需要更灵活的控制方式。于是,非递归遍历成为一种必要补充:通过显式地维护栈或队列来模拟递归过程,从而在不依赖系统栈的情况下完成遍历。本博客将介绍一些非递归的方式实现二叉树遍历的方法。

1. 节点定义

struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : val(0), left(nullptr), right(nullptr) {}TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

2. 先序遍历

二叉树的先序遍历遵循"根-左-右"的顺序。使用非递归方式实现时,我们需要借助来模拟递归过程。具体思路如为:

  1. 从根节点开始,将节点压入栈中;
  2. 执行以下步骤直至栈为空:
    • 取出栈顶节点并访问;
    • 如果该节点有右节点,则将右节点压栈;
    • 如果该节点有左节点,则将左节点压栈。
void preorder(TreeNode *root) {if (root == nullptr)return;stack<TreeNode *> st;st.push(root);while (!st.empty()) {TreeNode *node = st.top();st.pop();cout << node->val << endl;if (node->right != nullptr) {st.push(node->right);}if (node->left != nullptr) {st.push(node->left);}}
}

3. 中序遍历

二叉树的中序遍历遵循"左-根-右"的顺序。使用非递归方式实现时,同样需要借助来模拟递归过程。具体思路如下:

  1. 从根节点开始,将所有左节点依次压入栈中;
  2. 当没有左子节点时,弹出栈并访问;
  3. 转向该节点的右子树,重复步骤 1-2;
  4. 直到栈为空且当前节点为 null 时结束。
void inorderTraversal(TreeNode *root) {stack<TreeNode *> st;TreeNode *curr = root;while (curr != nullptr || !st.empty()) {while (curr != nullptr) {st.push(curr);curr = curr->left;}curr = st.top();st.pop();cout << curr->val << " ";curr = curr->right;}
}

4. 后续遍历

二叉树的后序遍历遵循"左-右-根"的顺序,是非递归遍历中最复杂的一种,因为需要确保在访问根节点之前,左右子树都已被访问。

4.1 方法一:使用两个栈

使用两个栈实现后序遍历的具体思路为:

  1. 初始化两个栈 s1s2,并将根节点压入 s1
  2. 循环执行直到 s1 为空:
    • s1 栈顶元素弹出,并压入 s2
    • 将弹出节点的左子节点和右子节点压入 s1
  3. 依次弹出 s2 中的所有节点并访问。
void postorderTraversal(TreeNode *root) {if (root == nullptr)return;stack<TreeNode *> s1, s2;s1.push(root);while (!s1.empty()) {TreeNode *node = s1.top();s1.pop();s2.push(node);if (node->left != nullptr) {s1.push(node->left);}if (node->right != nullptr) {s1.push(node->right);}}while (!s2.empty()) {cout << s2.top()->val << endl;s2.pop();}
}
4.2 方法二:使用一个栈

使用一个栈实现后序遍历的具体思路为:

  1. 从根节点开始,将所有左子节点压入栈中;
  2. 使用一个指针记录前一个访问的节点;
  3. 循环检查栈顶节点:
    • 如果当前的节点未访问,则转入右子树;
    • 如果当前的节点已访问,则访问当前节点,并将指针指向当前节点。
void postorderTraversal2(TreeNode *root) {if (root == nullptr)return;stack<TreeNode *> st;TreeNode *prev = nullptr;TreeNode *curr = root;while (curr != nullptr || !st.empty()) {while (curr != nullptr) {st.push(curr);curr = curr->left;}TreeNode *topNode = st.top();if (topNode->right != nullptr && topNode->right != prev) {curr = topNode->right;} else {cout << topNode->val << endl;prev = topNode;st.pop();}}
}
http://www.hskmm.com/?act=detail&tid=19998

相关文章:

  • 1208. 翻硬币
  • C# Avalonia 15- Animation- CachingTest
  • CF838D Airplane Arrangements
  • java操作数据库中的bug
  • 事务和Spring常用注解的总结
  • 浅谈SQL应用考试,临时抱佛脚篇
  • 网络安全风险评估指南:CISO如何通过风险评估提升安全防护
  • 藏好自己,做好清理——悼念沈劫匪先生有感
  • macbook m1 安装telnet
  • 低空经济:从政策热词到生活日常——中国低空经济全景解析与杭深模式对比 - 教程
  • 指数函数的特征
  • 生猪
  • git merge driver简介
  • 在 Linux 中安装和配置 NTP 服务器和 NTP 客户端
  • Android15音频进阶之车载多音区调整解析(一百三十七)
  • 微信二次开发社群机器人接口
  • FireDAC(Master-Detail 功能)主从表查询
  • 极氪汽车火山引擎:AI数据专家“上岗”,注入“分钟级”数据洞察力
  • C++面试宝典 01 new/delete/malloc/free关系
  • Ansible + Docker 部署 MinIO 集群
  • ​​万用表与电流探头测量电流信号的技术对比分析​​
  • flink运行时架构 - --
  • k8s命令
  • wifi亮灭屏机制--系统修改
  • 自动遍历测试利器:开源工具AppCrawler 配置全解析
  • 得帆云ETL全新版本升级驱动数据高效流转
  • Windows 的图标没有及时更新
  • 拒绝 “能源糊涂账”!MyEMS 如何让中小企业能耗管理 “秒上手”?
  • 【海内外多个支持单位|学生优惠|高录用快见刊】第五届管理科学和软件工程国际学术会议(ICMSSE 2025)
  • 电天下dq123.com搜索功能全新升级,AI加持,焕新垂直行业搜索体验!