回顾

问题陈述: 给定一棵二叉树,实现中序遍历并返回包含其中序序列的数组

例如给定下列二叉树:



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



最终中序遍历结果可以输出为: [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)空间复杂度的更多相关文章

  1. 数据结构《10》----二叉树 Morris 中序遍历

    无论是二叉树的中序遍历还是用 stack 模拟递归, 都需要 O(n)的空间复杂度. Morris 遍历是一种 常数空间 的遍历方法,其本质是 线索二叉树(Threaded Binary Tree), ...

  2. 二叉树 排序二叉树-可以通过中序遍历得到排序的数据 二叉排序树时间复杂度O(logn),

    二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加.但是他也有自己的缺点:删除操作复杂. 虽然二叉排序树的最坏效率是O(n),但它支持动 ...

  3. 数据结构-用C++实现一个二叉树,递归方法中序遍历

    1:二叉排序树,又称二叉树.其定义为:二叉排序树或者空树,或者是满足如下性质的二叉树. (1)若它的左子树非空,则左子树上所有节点的值均小于根节点的值. (2)若它的右子树非空,则右子树上所有节点的值 ...

  4. Leetcode 94. 二叉树的中序遍历

    1.问题描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 2.解法一 ...

  5. 二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)

    例题 中序遍历94. Binary Tree Inorder Traversal 先序遍历144. Binary Tree Preorder Traversal 后序遍历145. Binary Tre ...

  6. [LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历

    Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...

  7. LeetCode(94):二叉树的中序遍历

    Medium! 题目描述: 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗 ...

  8. leetcode 94二叉树的中序遍历

    递归算法C++代码: /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; ...

  9. 【LeetCode】94. 二叉树的中序遍历

    94. 二叉树的中序遍历 知识点:二叉树:递归:Morris遍历 题目描述 给定一个二叉树的根节点 root ,返回它的 中序 遍历. 示例 输入:root = [1,null,2,3] 输出:[1, ...

  10. [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 ...

随机推荐

  1. oeasy教您玩转vim - 9 - # 换行插入

    插入新行 回忆上节课内容 上上次是 i.I 在光标前面插入 又加了 a.A 可以在光标后面插入 a 是在光标后插入 A 是在当前行最后插入 关于插入,还有什么命令吗? 我们继续去查阅 help :h ...

  2. 「比赛记录」CF Round 954 (Div. 3)

    Codeforces Round 954 (Div. 3) 题目列表: A. X Axis B. Matrix Stabilization C. Update Queries D. Mathemati ...

  3. 把python中的列表转化为字符串

    怎么把python中的列表转换为字符串: 1,列表中非字符串的元素的转换 方法一: 使用列表推导式进行转换 1 list=['hello',6,9,'beizhi'] 2 list=[str(i) f ...

  4. mybatis:映射方式与SQL注入

    1.映射方式有两种,一种是resultType 这个是有一个实体类其成员与数据库中表字段一一对应,下例中就是User类对应了user表 <mapper namespace="com.e ...

  5. Jmeter函数助手34-digest

    digest函数用于返回特定哈希算法的加密值. 算法摘要:填入算法,如MD2.MD5.SHA-1.SHA-224.SHA-256.SHA-384.SHA-512 String to be hashed ...

  6. 【Spring-Cloud】Nacos2.0.3 单系统集群部署问题汇总

    1.强制要求JDK8版本 且 64位 C:\Users\Administrator\Desktop\Nacos-Server\Nacos-2.0.3-8848\bin>java -version ...

  7. 【DataBase】MySQL 13 分组查询

    视频参考自:P59 - P68 https://www.bilibili.com/video/BV1xW411u7ax 分组查询 GROUP BY -- group by 子句 -- 要注意!grou ...

  8. 【Uni-App】page.json 配置项一栏笔记

    官方文档 https://uniapp.dcloud.io/collocation/pages 一些配置项是全局的,也可以在页面对象中设置 { "pages": [ //pages ...

  9. 【Mybatis-Plus】05 条件构造器 ConditionConstructor

    理解: 原来叫条件构造器,我一直以为都是封装条件对象 即SQL的查询条件,不过都一样. 其目的是因为的实际的需求灵活多变,而我们的SQL的筛选条件也需要跟着变化, 但是有一些固定的字段固定的方式可以保 ...

  10. python3解析wav文件获取dtmf值

    操作系统 :Windows 10_x64 Python版本:3.9.2 从事FreeSwitch相关工作,大概率会遇得到DTMF,DTMF的传递方式有三种: In-band RFC2833 SIP-I ...