二叉树的 Morris 中序遍历——O(1)空间复杂度
回顾
问题陈述: 给定一棵二叉树,实现中序遍历并返回包含其中序序列的数组
例如给定下列二叉树:

我们按照左、根、右的顺序递归遍历二叉树,得到以下遍历:

最终中序遍历结果可以输出为: [3, 1, 9, 2, 4, 7, 5, 8, 6]
Morris trick
Morris 中序遍历是一种树遍历算法,旨在实现 O(1) 的空间复杂度,无需递归或外部数据结构。该算法应高效地按中序顺序访问二叉树中的每个节点,并在遍历过程中打印或处理节点值,而无需使用堆栈或递归。
关键思想是在 current node 与其对应的 rightmost node 之间建立临时链接
先来看下中序遍历的过程:

做法讨论
节点的中序前驱是左子树中最右边的节点。因此,当我们遍历左子树时,我们会遇到一个右子节点为空的节点,这是该子树中的最后一个节点。因此,我们观察到一种模式,每当我们处于子树的最后一个节点时,如果右子节点指向空,我们就会移动到该子树的父节点。

当我们当前处于某个节点时,可能会出现以下情况:
情况1:当前节点没有左子树
- 打印当前节点的值
- 然后到当前节点的右子节点

如果没有左子树,我们只需打印当前节点的值,因为左侧没有节点可遍历。之后,我们移至右子节点继续遍历。
情况 2:存在一棵左子树,并且该左子树的最右边的孩子指向空。
- 将左子树的最右边的子节点设置为指向当前节点。
- 移动到当前节点的左子节点。

在这种情况下,我们还没有访问左子树。我们从左子树的最右节点到当前节点建立一个临时链接。此链接可帮助我们稍后确定何时完成左子树的按序遍历。设置链接后,我们移至左子节点以探索左子树。
情况3:存在一棵左子树,并且该左子树的最右边的孩子已经指向当前节点。
- 打印当前节点的值
- 恢复临时链接(将其设置回空)
- 移动到当前节点的右子节点。

这种情况对于保持树结构的完整性至关重要。如果左子树的最右边的子节点已经指向当前节点,则意味着我们已经完成了左子树的按序遍历。我们打印当前节点的值,然后恢复临时链接以恢复原始树结构。最后,我们移动到右子节点继续遍历。
算法

步骤 1:初始化 current 来遍历树。将 current 设置为二叉树的根。
步骤 2:当前节点不为空时:如果当前节点没有左子节点,则打印当前节点的值并移动到右子节点,即将当前节点设置为其右子节点。
步骤 3: 当前节点有左孩子,我们找到当前节点的 in-order predecessor 。这个 in-order predecessor 是左子树的最右节点。
- 如果 in-order predecessor 的右孩子节点为空:
- 将 in-order predecessor 右孩子节点设置为当前节点。
- 移动到 current 的左孩子
- 如果 in-order predecessor 的右孩子不为空:
- 通过in-order predecessor 的右孩子设置为空
- 打印当前节点的值。
- 通过先前 in-order predecessor 的右孩子拿到 current , 然后移动到 cuurent 的右孩子节点
重复步骤 2 和 3,直到到达树的末尾。
代码实现
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <queue>
#include <map>
using namespace std;
// TreeNode structure
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
// Function to perform iterative Morris
// inorder traversal of a binary tree
vector<int> getInorder(TreeNode* root) {
// Vector to store the
// inorder traversal result
vector<int> inorder;
// Pointer to the current node,
// starting from the root
TreeNode* cur = root;
// Loop until the current
// node is not NULL
while (cur != NULL) {
// If the current node's
// left child is NULL
if (cur->left == NULL) {
// Add the value of the current
// node to the inorder vector
inorder.push_back(cur->val);
// Move to the right child
cur = cur->right;
} else {
// If the left child is not NULL,
// find the predecessor (rightmost node
// in the left subtree)
TreeNode* prev = cur->left;
while (prev->right && prev->right != cur) {
prev = prev->right;
}
// If the predecessor's right child
// is NULL, establish a temporary link
// and move to the left child
if (prev->right == NULL) {
prev->right = cur;
cur = cur->left;
} else {
// If the predecessor's right child
// is already linked, remove the link,
// add current node to inorder vector,
// and move to the right child
prev->right = NULL;
inorder.push_back(cur->val);
cur = cur->right;
}
}
}
// Return the inorder
// traversal result
return inorder;
}
};
int main() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->left->right->right = new TreeNode(6);
Solution sol;
vector<int> inorder = sol.getInorder(root);
cout << "Binary Tree Morris Inorder Traversal: ";
for(int i = 0; i< inorder.size(); i++){
cout << inorder[i] << " ";
}
cout << endl;
return 0;
}
二叉树的 Morris 中序遍历——O(1)空间复杂度的更多相关文章
- 数据结构《10》----二叉树 Morris 中序遍历
无论是二叉树的中序遍历还是用 stack 模拟递归, 都需要 O(n)的空间复杂度. Morris 遍历是一种 常数空间 的遍历方法,其本质是 线索二叉树(Threaded Binary Tree), ...
- 二叉树 排序二叉树-可以通过中序遍历得到排序的数据 二叉排序树时间复杂度O(logn),
二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加.但是他也有自己的缺点:删除操作复杂. 虽然二叉排序树的最坏效率是O(n),但它支持动 ...
- 数据结构-用C++实现一个二叉树,递归方法中序遍历
1:二叉排序树,又称二叉树.其定义为:二叉排序树或者空树,或者是满足如下性质的二叉树. (1)若它的左子树非空,则左子树上所有节点的值均小于根节点的值. (2)若它的右子树非空,则右子树上所有节点的值 ...
- Leetcode 94. 二叉树的中序遍历
1.问题描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 2.解法一 ...
- 二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)
例题 中序遍历94. Binary Tree Inorder Traversal 先序遍历144. Binary Tree Preorder Traversal 后序遍历145. Binary Tre ...
- [LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历
Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...
- LeetCode(94):二叉树的中序遍历
Medium! 题目描述: 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗 ...
- leetcode 94二叉树的中序遍历
递归算法C++代码: /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; ...
- 【LeetCode】94. 二叉树的中序遍历
94. 二叉树的中序遍历 知识点:二叉树:递归:Morris遍历 题目描述 给定一个二叉树的根节点 root ,返回它的 中序 遍历. 示例 输入:root = [1,null,2,3] 输出:[1, ...
- [LeetCode] Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树
Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...
随机推荐
- Webpack3.x升级至 4.x 小记
近期项目部署遇到点问题,需要升级webpack版本,特此整理一小记,记录升级过程中的依赖包及报错处理. 本次升级的依赖包及对应版本对照表: npm 包 当前版本 升级版本 S/D vue ^2.5.1 ...
- 【VMware VCF】VMware Cloud Foundation Part 01:概述。
VMware Cloud Foundation(简称 VCF)是 VMware 打造的一套用于 Software Defined Data Center(SDDC)软件定义数据中心的全栈云平台解决方案 ...
- Java maven构建命令使用总结
实践环境 Apache Maven 3.0.5 (Red Hat 3.0.5-17) maven构建生命周期 学习Maven构建命令之前,我们不烦先简单了解下Maven构建生命周期. Maven基于构 ...
- 记一次 Redisson 线上问题 → 你怎么能释放别人的锁
开心一刻 今天,我的又一个好哥们脱单了,只剩下我自己单身了 我向一个我喜欢的女生吐苦水 我:我这辈子是找不到女朋友了 她:怎么可能,你很优秀的,会有很多女孩子愿意当你女朋友的 我内心窃喜,问道:那你愿 ...
- ArcGIS for Android入门(Java):初体验
准备工作 开发工具:Android Studio 环境:jdk 11 (首次接触安卓开发,可能有的地方不太对,还请给位大佬多多指点) 项目搭建 打开Android Studio,点击New Proje ...
- 【XML】Extensible Markup Language 可扩展标记语言
Extensible Markup Language 可扩展标记语言[XML] 视频资料参考自:https://www.bilibili.com/video/BV1B441117Lu?p=186 其他 ...
- 【RabbitMQ】12 日志监控 & 消息追踪
一.日志和监控 RabbitMQ日志存放目录 [root@localhost ~]# ll /var/log/rabbitmq/ 总用量 176 -rw-r-----. 1 rabbitmq rabb ...
- 【C3】01 概述
CSS (层叠样式表) 让你可以创建好看的网页,但是它具体是怎么工作的呢? 这篇文章通过一些很简单的例子,告诉我们什么是 CSS, 同时还会涉及一些和 CSS 相关的专业术语. 预备知识: 基本的计算 ...
- Python的GDAL库绘制多波段、长时序遥感影像时间曲线图
本文介绍基于Python中的gdal模块,对大量长时间序列的栅格遥感影像文件,绘制其每一个波段中.若干随机指定的像元的时间序列曲线图的方法. 在之前的文章中,我们就已经介绍过基于gdal模块, ...
- 使用AI模型替代工业仿真过程
引自: https://www.zhihu.com/question/641951284/answer/3384531468 使用AI模型替代工业仿真,如:CAE,等等,进行仿真环境的求解运算.