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

二叉树的非递归遍历 | 秋招面试必备

文章目录

  • 前言
    • 基础设置
  • 前序遍历
  • 中序遍历
  • 后序遍历
    • 法一
    • 法二

前言

本文将详细介绍二叉树的非递归遍历的实现方法,利用迭代循环实现二叉树的不同遍历方式。
如果在开始前,已掌握递归遍历二叉树的方法,理解栈帧的建立和销毁过程,能够更容易上手迭代写法。

基础设置

下面的函数均为类BinaryTree的成员函数,_root为类的成员变量,在构造函数中初始化为二叉树的根节点。

二叉树节点声明如下:

struct TreeNode {TreeNode(int d = 1) : left(nullptr), right(nullptr), data(d){}TreeNode* left;TreeNode* right;int data;
};

前序遍历

前序遍历,即按照“根左右”顺序遍历二叉树。迭代法的核心是使用栈记录将要遍历到的树的节点,而栈具有“后进先出”的特性,所以为满足先访问左子树,在访问右子树的要求,则节点的入栈顺序为==“右左”==。
初学时,节点入栈的顺序,容易与递归遍历“根左右”顺序混淆。因为在循环体中,没有完全按照“右左根”顺序进行处理,依然是先处理了根节点。这就需要辩证的看待“根”这一概念,当前处理的根节点也可以是某一节点的左子树(右子树)。学习迭代遍历法时,选择性地规避“根”节点的概念,可能更好理解。

每次循环的栈顶,都为将要处理的根节点,然后再处理右子树(若不为空)、左子树(若不为空)。

void PreorderIt() {if (_root == nullptr) {return;}std::stack<TreeNode*> s;s.push(_root);while (!s.empty()) {TreeNode* cur = s.top();s.pop();std::cout << cur->data << ' ';if (cur->right != nullptr) {s.push(cur->right);}if (cur->left != nullptr) {s.push(cur->left);}}return;
}

也可以显示使用一个指针cur,以表示当前要处理的根节点。

void PreorderItV2() {if (_root == nullptr) {return;}std::stack<TreeNode*> s;TreeNode* cur = _root;while (!s.empty() || cur != nullptr) {if (cur == nullptr) {cur = s.top();s.pop();}std::cout << cur->data << ' ';if (cur->right != nullptr) {s.push(cur->right);}cur = cur->left;}return;
}

中序遍历

迭代法中序遍历处理节点的顺序与“左根右”顺序一致,处理所有的左子树,然后访问根节点,最后处理右子树。

void InorderIt() {if (_root == nullptr) {return;}TreeNode* cur = _root;std::stack<TreeNode*> s;while (!s.empty() || cur != nullptr) {// 入栈所有的左子树while (cur != nullptr) {s.push(cur);cur = cur->left;}// 访问根节点cur = s.top();s.pop();std::cout << cur->data << ' ';// 处理右子树cur = cur->right;}return;
}

后序遍历

法一

遵循“左右根”顺序,迭代法也需要先处理左子树、右子树,最后是根节点。但是左右子树需要经过根节点中转(会访问两次),为避免反复访问一侧子树的节点,我们可以加入一个哨兵节点dummy,记录已经处理过的子树,从而解决死循环的问题。

void PostorderIt() {if (_root == nullptr) {return;}TreeNode* cur = _root;TreeNode* dummy = nullptr;std::stack<TreeNode*> s;while (!s.empty() || cur != nullptr) {// 处理左子树while (cur != nullptr) {s.push(cur);cur = cur->left;}cur = s.top();// 右子树存在 且 没有访问过if (cur->right && cur->right != dummy) { // 处理右子树cur = cur->right;}else {std::cout << cur->data << ' ';dummy = cur;s.pop();cur = nullptr; // 一定需要置空,避免反复处理左子树}}
}

法二

后序遍历原为“左右根”,那么如果按照“根右左”的顺序访问节点并逆序输出,那么也能得到相同的效果。【两次逆序,负负得正】

void PostorderItV2() {if (_root == nullptr) {return;}std::vector<int> res;std::stack<TreeNode*> s;s.push(_root);while (!s.empty()) {TreeNode* cur = s.top();s.pop();res.push_back(cur->data);if (cur->left != nullptr) {s.push(cur->left);}if (cur->right != nullptr) {s.push(cur->right);}}std::reverse(res.begin(), res.end());for (auto num : res) {std::cout << num << ' ';}return;
}
http://www.dtcms.com/a/362753.html

相关文章:

  • Spring Bean
  • LLM面试50问:NLP/RAG/部署/对齐/安全/多模态全覆盖
  • R语言根据经纬度获得对应样本的省份
  • WPF依赖属性和依赖属性的包装器:
  • iOS混淆工具实战 视频流媒体类 App 的版权与播放安全保护
  • 安卓学习 之 gradle下载失败的解决方法
  • Elasticsearch面试精讲 Day 5:倒排索引原理与实现
  • 跨越产业技术障碍、创新制造模式的智慧工业开源了
  • 【开题答辩全过程】以宠物生活社区为例,包含答辩的问题和答案
  • 扩散模型驱动的智能设计与制造:下一场工业革命?
  • 最新!阿里财报电话会蒋凡与吴泳铭透露重要信息:淘宝闪购成绩斐然;零售与AI双轮驱动;阿里云推出“Agent Bay”新产品···
  • 物联网为何离不开天硕工业级SSD固态硬盘?
  • maven 常用指令
  • Corona渲染噪点终结指南:3ds Max高效去噪全攻略
  • 【3D 入门-3】常见 3D 格式对比,.glb / .obj / .stl / .ply
  • 通信中FDD和TDD的区别
  • 【SpringBootWeb开发】《一篇带你入门Web后端开发》
  • 242. 有效的字母异位词| 349. 两个数组的交集
  • 框架-SpringMVC-1
  • 手写Muduo网络库核心代码1-- noncopyable、Timestamp、InetAddress、Channel 最详细讲解
  • hive udf 执行一次调用多次问题
  • 算法 --- 模拟
  • HDFS读写机制深度解析:分布式存储的核心奥秘
  • Zookeeper分布式锁原理
  • java-设计模式-5-创建型模式-建造
  • 科普:为什么在开发板上运行 Qt 程序时需要在命令后加 -platform linuxfb
  • 少儿舞蹈小程序从0到1(5):搭建关于我们页面
  • 深入浅出 RabbitMQ - SpringBoot2.X整合RabbitMQ实战
  • 23种设计模式-抽象工厂模式
  • 蓝桥杯算法之基础知识(4)