leetcode(94) 二叉树的中序遍历——莫里斯遍历(非传统递归方法)

图解二叉树的Morris(莫里斯)遍历 Morris 的整体思路就是将 以某个根结点开始,找到它左子树的最右侧节点之后与这个根结点进行连接。时间复杂度:O(n),额外空间复杂度:O(1) 阅读详情

题目描述
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1

2
/
3

输出: [1,3,2]

思路

二叉树的遍历用递归方法解十分简单。但是计算机是非常不愿意做递归运算的,因为有时候他占用空间是非常恐怖的,数字大了之后会非正常退出。
这里提一下先序遍历的非递归解法:使用栈

  • a. 遇到一个节点,访问它,然后把它压栈,并去遍历它的左子树;
  • b. 当左子树遍历结束后,从栈顶弹出该节点并将其指向右儿子,继续a步骤;
  • c. 当所有节点访问完即最后访问的树节点为空且栈空时,停止。
void PreOrderTraversal(BinTree * BT)
{
    BinTree * T = BT;
    stack<BinTree> S ;    //创建并初始化堆栈S
    while(T || ! IsEmpty(S))
    {
        while(T)        //一直向左并将沿途节点访问(打印)后压入堆栈 
        {
            printf("%d\n", T->Data);
            S.push(S, T);
            T = T->Left;
        }
        if (! IsEmpty(S))
        {
            T = S.top();    //节点弹出堆栈
            S.pop();
            T = T->Right;  //转向右子树
        }
    }
}
 
//更简单的非递归前序遍历
void preorderTraversalNew(TreeNode *root, vector<int> &path)
{
    stack< pair<TreeNode *, bool> > s;
    s.push(make_pair(root, false));
    bool visited;
    while(!s.empty())
    {
        root = s.top().first;
        visited = s.top().second;
        s.pop();
        if(root == NULL)
            continue;
        if(visited)
        {
            path.push_back(root->val);
        }
        else
        {
        //甚至不同的遍历只需要改动这里的顺序就可以
            s.push(make_pair(root->right, false));
            s.push(make_pair(root->left, false));
            s.push(make_pair(root, true));
        }
    }
}

中序遍历的非递归解法:莫里斯遍历
本方法中,我们使用一种新的数据结构:线索二叉树。方法如下:

Step 1: 将当前节点current初始化为根节点
Step 2: While current不为空,

若current没有左子节点
   a. 将current添加到输出
   b. 进入右子树,亦即, current = current.right
否则
   a. 在current的左子树中,令current成为最右侧节点的右子节点
   b. 进入左子树,亦即,current = current.left

举例而言:

      1
    /   \
   2     3
  / \   /
 4   5 6

首先,1 是根节点,所以将 current 初始化为 1。1 有左子节点 2,current 的左子树是

     2
    / \
   4   5

在此左子树中最右侧的节点是 5,于是将 current(1) 作为 5 的右子节点。令 current = cuurent.left (current = 2)。 现在二叉树的形状为:

     2
    / \
   4   5
        \
         1
          \
           3
          /
         6

对于 current(2),其左子节点为4,我们可以继续上述过程

    4
     \
      2
       \
        5
         \
          1
           \
            3
           /
          6

由于 4 没有左子节点,添加 4 为输出,接着依次添加 2, 5, 1, 3 。节点 3 有左子节点 6,故重复以上过程。 最终的结果是 [4,2,5,1,6,3]。


/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        TreeNode * curr = root;
        while(curr != NULL){
            if(curr->left == NULL){
                ret.push_back(curr->val);
                curr = curr-> right;
            }
            else{
                TreeNode * pre = curr -> left;
                while(pre->right != NULL){
                    pre = pre -> right;
                }
                pre -> right = curr;
                TreeNode * tmp = curr;
                curr = curr -> left;
                tmp -> left = NULL;
            }
        }
        return ret;
    }
};

后序遍历的非递归:
对于一个节点而言,要实现访问顺序为左儿子-右儿子-根节点,可以利用后进先出的栈,在节点不为空的前提下,依次将根节点,右儿子,左儿子压栈。
故我们需要按照根节点-右儿子-左儿子的顺序遍历树,而我们已经知道先序遍历的顺序是根节点-左儿子-右儿子,故只需将先序遍历的左右调换并把访问方式打印改为压入另一个栈即可。最后一起打印栈中的元素。

/**
    * 后序遍历 非递归
    * 双栈法
    * @param root
    */
   public static void postOrder2(Node root) {
       Stack<Node> stack = new Stack<Node>();
       Stack<Node> output = new Stack<Node>();
       Node node = root;
       while (node != null || !stack.isEmpty()) {
           if (node != null) {
               stack.push(node);
               output.push(node);
               node = node.rightNode;
           } else {
               node = stack.pop();
               node = node.leftNode;
           }
       }

       while (output.size() > 0) {
           Node n = output.pop();
           System.out.print(n.data + "\t");
       }
   }
 
//更简单的非递归后序遍历
void postorderTraversalNew(TreeNode *root, vector<int> &path)
{
    stack< pair<TreeNode *, bool> > s;
    s.push(make_pair(root, false));
    bool visited;
    while(!s.empty())
    {
        root = s.top().first;
        visited = s.top().second;
        s.pop();
        if(root == NULL)
            continue;
        if(visited)
        {
            path.push_back(root->val);
        }
        else
        {
            s.push(make_pair(root, true));
            s.push(make_pair(root->right, false));
            s.push(make_pair(root->left, false));
        }
    }
}
中序线索化二叉树的非递归算法 中序线索化二叉树的非递归算法:设计技巧:依某种顺序遍历二叉树,对每个结点p,判断其左指针是否为空,以及其前驱结点pre的右指针是否为空。 立即下载

相关推荐

【解决问题】‘vue‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

【解决问题】'vue' 不是内部或外部命令,也不是可运行的程序或批处理文件。

weixin_44566194的博客 4万+

Morris遍历的图示理解以及代码实现

文章目录Morris遍历的图示理解以及代码实现1、遍历规则2、复杂度分析3、代码实现以及分析4、Morris遍历实现前中后序遍历 Morris遍历的图示理解以及代码实现 1、遍历规则 假设当前正在遍历的节点是cur,那么cur的移动规则如下: 如果cur没有左孩子,则cur向右移动,即cur = cur.right。 如果cur有左孩子,找到cur左子树上最右边的节点,将这个节点记为mostRight,根据mostRight的情况继续分为下面两种情况: 如果mostRight的右孩子为null,则让mo

何时立秋的博客--向前有路,未来可期 1082

携程旅行网景区,评论数据爬虫项目数据库保存附源码

这是一个专门用于爬取某旅行网景区评论数据的Python爬虫项目。项目采用模块化设计,支持MySQL数据库存储,具备完整的异常处理机制和反爬虫策略。

ymqzhy的博客 2568

【算法】学习笔记(三)----Morris前序遍历、中序遍历、后序遍历(C++代码)

Morris遍历,也称为莫里斯遍历,是一种使用线索二叉树实现的二叉树遍历方法,可以在不使用栈或递归的情况下完成对二叉树的遍历。Morris遍历方法的核心思想是利用每个节点中存储的指向父节点的指针,将左子树中最右侧节点的指向父节点的指针指向当前节点,以便在访问完当前节点的左子树后,能够通过这个指向父节点的指针回到当前节点。这样就。

weixin_50202509的博客 1424

VCSA 6.7证书更新全流程指南:从故障诊断到完整替换

本文详细介绍了VCSA 6.7证书更新的全流程,包括故障诊断、证书替换操作步骤及常见问题解决方案。通过使用官方脚本和证书管理器,帮助管理员快速解决证书过期问题,确保vCenter系统的稳定运行。特别强调了STS证书修复和全量证书重置的关键步骤,适用于vcsa6.7环境下的证书管理需求。

weixin_42634811的博客 449

Linux音频调试实战:ALSA架构与tinymix工具的高级应用技巧

本文深入探讨了Linux音频调试的核心技术,重点解析了ALSA架构下音频信号的路由与混合机制。通过介绍轻量级命令行工具tinymix的高级应用技巧,指导开发者如何精准排查无声、音量异常、多路音频共存等复杂问题,并分享编写自动化测试脚本以提升嵌入式及智能设备音频开发效率的实战方法。

weixin_29210727的博客 100

数据结构与算法:二叉树—莫里斯遍历(Morris)

我的第一篇博客

volatile-code 852

从入门到精通:HyperMesh实体选择全攻略(含2025新界面操作)

本文系统解析了HyperMesh从基础到高级的实体选择技巧,重点介绍了2025新界面的交互逻辑与效率提升。内容涵盖选择模式、框选方法、右键菜单高级选择、图形窗口选择器与命令面板的智能联动,以及利用浏览器和过滤器进行精准定位的实战指南,帮助用户显著提升前处理建模效率。

open4的博客 1080

二叉树之Morris遍历

二叉树的Morris遍历的核心思想+遍历流程+前、中、后序的实现

qq_40478662的博客 488

博途 V16(TIA portal V16)安装教程

本文提供了西门子TIA Portal V16软件的完整安装指南。内容包括百度网盘下载链接(提取码kv69)、详细安装步骤(关闭安全保护、典型安装设置)、授权破解方法,以及PLCSIM仿真功能的使用说明。重点演示了S7-1200 PLC程序的仿真过程,解析了仿真界面各元素功能,包括状态指示灯(RUN/STOP/ERROR)、操作按钮(RUN/STOP/MRES)和网络接口状态等。通过图文并茂的方式,完整呈现了从软件安装到PLC程序仿真的全流程操作。

lingchen1906的博客 6665

基于本机知识库 + 豆包(火山引擎)+ MCP的落地方案

最小 MVP:本地知识库 + Doubao Embedding + 简单检索。工具化:把检索/写入/系统功能通过 MCP 封装。Agent 编排:RAG 流程 + 自检 + 引用输出。这样你就可以快速落地一个基于豆包 + 本机知识库的 MCP Agent。

lpfasd123的博客 1095

每日算法总结——二叉树的Morris遍历

一种遍历二叉树的方式, 并且时间复杂度ON额外空间复杂度O1,通过利用原树中大量空闲指针的方式,达到节省空间的目的Morris遍历细节假设来到当前节点cur,开始时cur来到头节点位置curcurcurmostRightmostRightcurcurmostRightcurnullcurcur通常的二叉树遍历是通过栈来完成的,也就是栈记录了之前节点的位置,方便遍历完下面的节点后返回,这种方式是需要占用大量的系统空间的。而看到遍历过程可知,它是利用了底层树的指向。

qq_53661372的博客 262

STM32 TIM3硬件XOR+复位模式实现霍尔信号精准捕获

霍尔传感器是电机控制系统中获取转子位置的关键器件,其三路数字信号需通过高精度、低延迟的边沿检测机制转换为电角度信息。其核心原理在于利用定时器输入XOR逻辑合并多路信号,并结合从模式复位(Reset Mode)实现硬件级时间间隔测量——每次跳变强制计数器清零,使CNT值直接表征换相周期。该方案规避了软件轮询的不确定性,显著提升FOC控制的实时性与鲁棒性。典型应用场景包括BLDC/FOC电机驱动、机器人关节伺服及工业变频器设计,尤其适用于STM32F4系列等具备硬件XOR与从模式控制器的MCU平台。

weixin_28931507的博客 91

二叉树的Morris遍历

利用叶子节点的left , right 这些空指针,使得非叶子节点能够借助这些空指针遍历两次,没有使用额外的空间。前中后遍历都是在第一次 / 第二次 的遍历过程中做不同的处理。Morris遍历可以将非递归遍历中的空间复杂度降为O(1)。从而实现时间复杂度为O(N),而空间复杂度为O(1)的精妙算法。对于没有左子树的节点之遍历一次,有左子树的节点遍历两次。:记作当前节点为cur。

这个人很懒,什么都没有写 360

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

彻底理解被称为二叉树神级遍历的莫里斯(Morris)算法

二叉树的遍历我们已经介绍过了最常见的递归遍历:用Java描述数据结构之二叉树,前序遍历,中序遍历,后序遍历 和迭代遍历二叉树前序,中序,后序遍历的迭代实现,实现思路及代码 上面两种遍历虽然看上去不同,但是底层的思路是大同小异的,都是调用栈,递归是用java虚拟机栈,而迭代使用我们自己创建的栈。今天要介绍的莫里斯算法,它的背后没有用任何栈相关的知识,而且相比于前两种空间复杂度为O(n)(因为进栈最差会所有数据全部进栈)的遍历来说,莫里斯算法的空间复杂度只有O(1),这对于量很大的数据来说无疑是天上地下的区别。

qq_46209845的博客 7199

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

二叉树遍历之图解Mirror算法(莫里斯算法)

我们写二叉树的遍历时,一般有两种方式,迭代和递归。然而还有一种神奇的算法,也可以作我们的二叉树递归,且空间复杂度为O(1),要知道,我们迭代和递归都是需要额外栈空间的

small_engineer的博客 2740

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

莫里斯中序遍历(Morris Inorder Traversal)算法详细解读

莫里斯中序遍历(Morris Inorder Traversal)是一种在不使用递归或栈的情况下对二叉树进行中序遍历的方法。它的核心思想是利用二叉树中的空闲右子指针建立临时连接(即线索树),以便在回溯时找到前驱节点。

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 1789

莫里斯前中后序遍历

【代码】莫里斯前中后序遍历。

qq_42259971的博客 327

线索二叉树和非递归遍历(中序)

基于中序遍历的线索二叉树和非递归遍历 #include <stdio.h> #include <malloc.h> #define MaxSize 100 typedef char ElemType; typedef struct node { ElemType data; int ltag,rtag; //增加的线索标记 struct node *lchild; struct node *rchild; } TBTNode; void CreateTBTNo

Lau's Blog 1404

二叉树中序遍历非递归算法

我们知道,在深度搜索遍历的过程中,之所以要用递归或者是用非递归的栈方式,参考二叉树非递归中序遍历,都是因为其他的方式没法记录当前节点的parent,而如果在每个节点的结构里面加个parent 分量显然是不现实的,那么Morris是怎么解决这一问题的呢?好吧,他用得很巧妙,实际上是用叶子节点的空指针来记录当前节点的位置,然后一旦遍历到了叶子节点,发现叶子节点的右指针指向的是当前节点,那么就认为以当前

serena_0916的博客 2305

中序遍历二叉树的非递归算法

        花了一点时间,将书本上中序遍历二叉树的非递归算法的伪代码翻译成纯c的可执行程序,算法要结合栈和树的基本操作函数。程序在vc6.0下调试通过。代码如下: #include stdio.h>#include stdlib.h>#define STACK_INIT_SIZE 100#define STACKINCREMENT 10#define OVERFLOW -2#d

天之痕 4266

数据结构 - 前中后序遍历

方法三:莫里斯遍历:在第二次到达时,然后逆序打印左子树的右边界,最后打印整棵树的右边界。如果cur有左孩子,找到cur左子树上最右的节点,记为mostRight。如果一个节点有左子树,可以到达两次,而且在第二次到达时,其左子树遍历完毕。如果cur无左孩子,cur向右移动, cur = cur.right。在介绍具体代码之前,先简述一下 莫里斯遍历 相关内容。若没有左子树,则只能到达一次。来到当前节点,记为cur。:不使用任何辅助空间。

weixin_43191747的博客 419

【leetcode系列】【算法】【中等】二叉树的中序遍历(递归,栈,莫里斯morris)

题目: 题目链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ 解题思路: 方法一:递归(时间O(N),空间O(N)) 每次递归,如果有左子树,就继续使用左子树继续递归 如果没有左子树,就将当前值加入到结果集中,返回到上一层递归中 在上一层递归中,先将当前值加入到结果集中,继续判断是否有右子树...

songyuwen0808的博客 1084

Morris遍历

是的,首先对于没有左子树的节点,计算它们时不需要向下访问,只需要对有左子树的节点进行向下两次访问,都是找左子树的最右节点,这样子可以得出二叉树上的节点被访问2次,2是个常数,因此时间复杂度为O(N)。首先要明确求当前节点在那一层,根据Morris遍历,树上的节点要么是从父节点到达,要么是从左子树的最右节点来的。因为有左子树就有左子树的最右节点,最右节点指向空时,就让其指向cur,进行“搭桥”,为第二次的访问做准备,但是如果没有左子树,遍历过程不会回到该节点,因此就不会再次访问到cur节点,所以到达一次。

2301_79985750的博客 1232

宽屏手机“看得更多“,我 16:9 的凭什么吃亏?

"宽屏看得多"是个三角函数问题,但**"要不要让宽屏看得多"是个设计问题**。单机游戏可以拥抱 Hor+,让好设备获得更沉浸的画面——那是奖励。竞技射击游戏不行,因为在这里视野就是信息,信息就是胜负。把"设备差异"限制在体验层(更舒服的按键、更宽的观感),坚决挡在竞技层(可见敌人、可用信息)之外。面积守恒的 FOV 曲线 + 彻底解耦的开镜系统 + 与屏幕无关的服务端 AOI + 可量化的比例矩阵门禁。一条公式引发的问题,最终要靠一整套工程纪律来关上。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 3万+

二叉树的非递归遍历(二)之线索二叉树

中序线索二叉树的构造: typedef struct TBNode { int data; int ltag,rtag; TBNode *lchild; TBNode *rchild; }TBTNode; //通过中序遍历对二叉树线索化 void InThread(TBTNode *p,TBTNode *&pre) { if(p!=NULL) { InThread(...

xiaoyao_zhang的博客 936
上一篇: leetcode(85)最大矩形——转化为84题
下一篇: leetcode(96)不同的二叉搜索树——卡塔兰数
softwareX4
博客等级 码龄9年 21粉丝 63原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值