首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

二叉树的 Morris 中序遍历——O(1)空间复杂度

编程知识
2024年09月15日 13:36

回顾

问题陈述: 给定一棵二叉树,实现中序遍历并返回包含其中序序列的数组
例如给定下列二叉树:
image
我们按照左、根、右的顺序递归遍历二叉树,得到以下遍历:
image
最终中序遍历结果可以输出为: [3, 1, 9, 2, 4, 7, 5, 8, 6]

Morris trick

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

做法讨论

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

当我们当前处于某个节点时,可能会出现以下情况:

情况1:当前节点没有左子树

  • 打印当前节点的值
  • 然后到当前节点的右子节点
    image
    如果没有左子树,我们只需打印当前节点的值,因为左侧没有节点可遍历。之后,我们移至右子节点继续遍历。

情况 2:存在一棵左子树,并且该左子树的最右边的孩子指向空。

  • 将左子树的最右边的子节点设置为指向当前节点。
  • 移动到当前节点的左子节点。
    image
    在这种情况下,我们还没有访问左子树。我们从左子树的最右节点到当前节点建立一个临时链接。此链接可帮助我们稍后确定何时完成左子树的按序遍历。设置链接后,我们移至左子节点以探索左子树。

情况3:存在一棵左子树,并且该左子树的最右边的孩子已经指向当前节点。

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

算法

image
步骤 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;
}
From:https://www.cnblogs.com/sparkyen/p/18415231
本文地址: http://shuzixingkong.net/article/2039
0评论
提交 加载更多评论
其他文章 1Panel:一个现代化、开源的 Linux 服务器运维管理面板
前言 之前有小伙伴问:Linux 服务器运维管理除了宝塔,还有其他值得推荐的管理软件吗?,今天大姚给大家分享一个现代化、开源的 Linux 服务器运维管理面板:1Panel。 项目介绍 1Panel是新一代的 Linux 服务器运维管理面板,旨在通过现代化的 Web 界面帮助用户轻松管理 Linux
1Panel:一个现代化、开源的 Linux 服务器运维管理面板 1Panel:一个现代化、开源的 Linux 服务器运维管理面板 1Panel:一个现代化、开源的 Linux 服务器运维管理面板
Nuxt Kit 组件管理:注册与自动导入
title: Nuxt Kit 组件管理:注册与自动导入 date: 2024/9/15 updated: 2024/9/15 author: cmdragon excerpt: Nuxt Kit 为组件的注册和导入提供了灵活高效的解决方案。无论你是要批量导入组件,还是单独处理特定组件,这些工具都能
Nuxt Kit 组件管理:注册与自动导入 Nuxt Kit 组件管理:注册与自动导入
深度!程序员生涯的垃圾时间(上)
深度好文!程序员的垃圾时间! 垃圾时间(Garbage time)是体育赛事中的术语,指一场比赛中双方分差过大,胜负已定。此时,比赛剩余的时间不再对最终结果产生决定性影响,剩下的时间就被称为垃圾时间。将这个词用在浩浩荡荡的技术革命、汹涌向前的历史车轮上,再合适不过。时代的必然,是个人无法违背的规律。
.NET 的 Native AOT 现在是什么样的?
在软件开发领域,优化性能和简化效率仍然至关重要。多年来,.NET 平台一直在创新,为开发人员提供基础设施,以打造弹性和高效的软件解决方案。今天要写的这篇文章源自昨天在朋友圈发的文章《UWP 通过 .NET 9 和Native AOT 的支持实现 UWP 应用的现代化》[1],一位小伙伴的对话让我想全
.NET 的 Native AOT 现在是什么样的? .NET 的 Native AOT 现在是什么样的?
这些年没来得及学习的一些 HTML5 标签
认识并学习下还没来得及学习的一些 HTML5 标签 &lt;ruby&gt; 标签 HTML&#160;&lt;ruby&gt;&#160;元素被用来展示东亚文字注音或字符注释。 比如: &lt;ruby&gt;兄弟&lt;rt&gt;xiongdi&lt;/rt&gt;&lt;/ruby&gt;
这些年没来得及学习的一些 HTML5 标签 这些年没来得及学习的一些 HTML5 标签 这些年没来得及学习的一些 HTML5 标签
MySQL 大表拆分
概述 在实际工作中,在关系数据库(MySQL、PostgreSQL)的单表数据量上亿后,往往会出现查询和分析变慢甚至无法执行统计分析的情况。这时就需要将大表拆分为多个小表,将小表分布在多个数据库上,形成一个数据库集群。这样的话,一条 SQL 统计语句就可以在多台服务器上并发执行,然后将执行结果汇总,
MySQL 大表拆分 MySQL 大表拆分 MySQL 大表拆分
manim边学边做--弧形多边形
弧形多边形是一种结合了圆弧和多边形的图形,这类几何图形在设计中应用非常广泛。 比如在家居设计中,看看家里的沙发,餐桌和座椅等,它们的边角,靠背等地方都是弧形的设计,这种设计有效柔化了室内空间,使整体氛围更加和谐自然。 还有景观和建筑设计中,弧形多边形常被用于道路规划、花坛布局等, 特别是儿童游乐的区
manim边学边做--弧形多边形 manim边学边做--弧形多边形 manim边学边做--弧形多边形
MonoDevelop 的续集dotdevelop
DotDevelop 是一个跨平台的 .NET 集成开发环境(IDE),它原本是 MonoDevelop 的分支项目,这个项目更侧重于 Linux 支持和 GTK3 升级,github:https://github.com/dotdevelop/dotdevelop。MonoDevelop 是一个开