二叉树的 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 ...
随机推荐
- UE4 WebUI使用指南2-通信
前面一篇WebUI的文章讲述的WebUI插件的下载,开启,在UE中创建,加载网页等. 本文继续讲述通过WebUI,UE和网页实现双向通信的实现思路. 一点说明 由于WebUI 使用的浏览器内核并不是最 ...
- tp5框架No input file specified
最近从网上下载了一个项目,本地搭建好环境.访问页面出现No input file specified. 这个问题之前就遇到过,是因为权限的问题,导致nginx无法解析php文件,这次有点不一样所以记录 ...
- TP5 连接多个数据库
use think\Config; $config = Config::get('database2'); //读取第二个数据库配置 $connect = Db::connect($config); ...
- [oeasy]python0021_python虚拟机的位置_可执行文件_转化为字节形态
程序本质 回忆上次内容 \n 就是换行 他对应着 ascii 字符的代码是(10)10进制 他的英文是 LF,意思是Line Feed 我们可以在<安徒 ...
- 企业级环境部署:在 Linux 服务器上如何搭建和部署 Python 环境?
在大部分企业里,自动化测试框架落地都肯定会集成到Jenkins服务器上做持续集成测试,自动构建以及发送结果到邮箱,实现真正的无人值守测试. 不过Jenkins搭建一般都会部署在公司的服务器上,不会在私 ...
- vue项目中实现sql编辑器功能自定义高亮词汇可提示关键词-codemirror
先上图:左侧是数据库表,右侧上部是sql编辑器,下部是执行sql的返回接口 HTML: <el-row> <el-col :span="4" class=&quo ...
- 解决 IIS Express 启动错误:“拒绝访问”问题
报错 Starting IIS Express ... stderr: Failed to register URL "http://localhost:8378/" for si ...
- 【Spring-Security】Re08 Thymeleaf权限控制 与 退出功能
一.需要的组件支持: 新版本这里的组件有些问题: https://blog.csdn.net/qq_36488647/article/details/104532754 https://blog.cs ...
- windows11系统NVIDIA显卡驱动自动升级导致2070 Super显卡失效 —— 552.22版本自动升级到560.70版本后2070 Super型号显卡停止工作
操作系统 Windows11,旧版本显卡驱动是552.22,由于安装的是NVIDIA Geforce Experience后显卡驱动自动升级到560.77版本,然后显卡不再工作. 重新安装显卡驱动56 ...
- 【转载】人工智能CAE仿真分析技术
原文: https://cloud.tencent.com/developer/news/628731 AI与CAE相结合 CAE的本质是对复杂工程问题通过合理简化建立数学模型,并根据输入求得输出.深 ...