逆转链方法遍历二叉树 不使用栈与递归 Robson方法 与 Morris方法

二叉树遍历高级算法之Morris---莫里斯算法 莫里斯算法与线索二叉树有异曲同工之妙,建议先了解线索二叉树,再来学习 Morris算法莫里斯算法思想前序遍历中序遍历后序遍历 莫里斯算法思想 mirror遍历用到了线索二叉树的思想,在Morris方法中不需要为每个节点额外分配指针指向其前(predecessor)和后继节点(successor),只需要利用叶子节点中的左右空指针指向某种顺序遍历下的前驱节点或后继节点就可以了 。 Morris的整体思路就是将 以某个根结点开始,找到它左子树的最右侧节点之后与这个根结点进行连接 我们可以从 图2 看到, 阅读详情

部分图文转自: http://hi.baidu.com/chenxiong0115/blog/item/2ace8512751e950f5baf53e5.html

参考: http://blog.youkuaiyun.com/billfranck/article/details/4525633



一、J.M.Robson方法 

基于逆转链二叉树遍历思想,非递归无额外栈的二叉树遍历算法。

    该算法是非递归的,指所有标题中无栈打了引号,是因为该算法其实是使用了栈的,只不过它充分利用了二叉树的叶子节点,算法中二叉树的叶子节点就是栈节点。

下图为实例与说明:


说明:

(a)初始化指向根的节点SuperRoot,cur指向当前访问节点,dad始终指向cur的父节点。cur一直向下访问,直到第一个叶子节点D,由于任一叶子节点X进栈的条件是:存在一个出度为2的节点Y,满足X在Y的左子树中;X进栈的时机是:从X处向上回溯,dad第一次遇到满足条件的节点Y。因此,用一临时指针av暂时保存将可能进栈的叶子节点X。

(b)中av指向节点D。

(c)中dad回溯至节点A,此时av指向的叶节点D入栈,并将D的右指针指向节点A,当dad再次回溯到节点A时就可根据栈指针标志判断出是从右子树回溯。

从图c到图d,cur转向dad的右子树继续向下遍历。图e,cur访问叶节点G,G为将可能入栈节点,用av标记。图f, dad回溯至节点C,节点G入栈,将G的右指针指向节点C。图f到图g,cur右转,遇叶节点F,用av标记,向上回溯。由于此时dad与当前栈顶右指针指向同一节点C,说明已从C左子树回溯过,本次是从C的右子树回溯,故退栈,接续向上回溯。图h中,因dad==top->LeftChild,退栈,继续向上回溯。如图i,dad回溯到SuperRoot,遍历完,然后释放节点SuperRoot在堆区的内存,算法结束。

设节点个数为N,该算法时间开销为O(N) 空间开销为O(1)。由于遍历时改变了树结构,在多线程环境中使用可要注意了。

非递归的二叉树遍历重点在于回溯,有两个问题:其一,需要找到父节点,其二,需要告知父节点,本节点是左子节点还是右子节点。第一个问题可以使用Parent指针,肯定比逆转链快,但增加了空间消耗。 第二个问题一般是用栈来解决,Robson算法也是如此。



采用逆转链的方法,即在遍历深入时,每深入一层,就将其再深入的孩子结点的地址取出,并将其双亲结点的地址存入,// 当深入不下去需返回时,可逐级取出双亲结点的地址,沿原路返回。

void  intrace( Node* p){
	if( NULL==p) return;
	// dad:当前指针的父亲,top:最后一次访问的叶子(模拟的栈顶),deg2:第一个度为2的父节点
	// av:可能的入栈叶子节点
	Node *dad=p, cur=p->lc, top=NULL, deg2=NULL, av=NULL, tmp=NULL; 
	for(;;){
		// if( NULL!=dad->lc && NULL!=dad->rc )   deg2 = dad; // 更新
		if( cur ){
			while ( cur->lc ){  // 向左走到底
				// dad = p;
				if( NULL!=dad->lc && NULL!=dad->rc )   deg2 = dad; // 更新deg2节点
				tmp = cur->lc;  curl->lc = dad;  dad = cur;   // 更新dad,设置lc指针为回溯指针
				cur = tmp;   // 向下遍历
			}
			cout << *cur << endl; // 访问该节点
			if( cur-> rc ){    // 处理右子树 
				cur->lc = dad; 
				dad = cur;  cur = cur->rc;  // 转向右子树
			}
			else{  // cur为叶子节点. ??如何判断该
				// av = cur; 
				if( deg2==top->rc ){  // 叶子从右子树进入
					// 退栈
					top = top->lc; 
					cur = deg2->rc;  dad = cur->lc ;  // 当前子树已经遍历完,退到更高一层
					if( p==dad )    // 回到树根,所有树枝已经遍历,终止
						break; 
				}
				else{   
					// 更新top指针,并记录前一个top到左子树
					tmp = top;  top = cur;    top->lc = tmp ; 
					// 叶子的右孩子指向deg2(或NULL),用于访问右子树时判断该deg2已经遍历完
					cur->rc = deg2; 
					// 回溯并访问,直到deg2节点,然后计入上面的if(cur->rc)语句中
					while( deg2!=cur ){  
						tmp = cur;  cur = dad;  cur->lc=tmp;     // 回溯,并恢复父亲的左孩子
						cout << *cur << endl;  // 访问
					}
					dad = cur;  cur = cur->rc ;   // 转向右子树
				}
			}
		}
		else{   // 无左子树
			// cout << *cur << endl;
			cur = dad; dad = dad->lc;  // 回溯到原始树根,dad指向NULL
			cout << *cur << endl;
			cur=cur->rc;  
                }
	}

}

二、  Morris方法

原理: 每个有左分支的子树,该树根的前缀为左子树的最右侧叶子节点。刚进入该子树时,先将该叶子节点的 rightchild  指针 rc  指向子树的树根 p, 对p 从左子树向右子树遍历时,如果叶子指向 p ,则表示该子树已经遍历完成。

以下为C++实现:

void InOrder(Node* p, status (*vis)(Data) ){
	Node *tmp=NULL;
	while(NULL!=p){
		if(NULL==p->lc){  
			vis(p->data);
			p=p->rc;   // 如果为叶子,则跳回其父节点
		}
		else{
			tmp = p->lc; //  p为根的左子树
			while( NULL!=tmp->rc && p!=tmp->rc )  //左子树的最右叶子
				tmp = tmp->rc;
			if( NULL==tmp->rc ){
				tmp->rc = p; // 将p的前驱指向p
				p = p->lc ;  // 遍历左子树
			}
			else{          // 左子树已经遍历完
				vis(p->data);
				tmp->rc = NULL;  // 还原p的前驱叶子的右孩子指针
				p = p->rc; 
			}
		}
	}
}




数学建模学习(75):全局敏感性分析Morris 方法 敏感性分析 (SA) 是分析不同应用条件下模型行为的基本实践。提出了许多 SA 技术,从简单的筛选方法到计算成本高昂的基于方差的方法。本篇内容我们通过将 Morris 方法来对模型进行分析。 阅读详情

相关推荐

灵敏度分析 - Morris 方法(简单):Morris 方法在任何 Matlab 函数中的应用-matlab开发

敏感性分析用于估计不确定因素对函数输出的影响。 莫里斯(Morris)方法有时被称为定性方法:它以有限的计算次数给出了粗略的估计。 作为第一步,Morris 方法可用于简化函数。 它可以识别可以修复的影响较小的因素。 了解更多信息 : Saltelli, A.、Tarantola, S.、Campolongo, F. 和 Ratto, M. (2004)。 实践中的敏感性分析 - 评估科学模型的指南。 威利。 此功能尊重来自以下方面的建议: Sohier, H.、Farges, JL 和 Piet-Lahanier, H.(2014 年 8 月)。 改进空中发射到轨道分离的莫里斯方法的代表性。 在第 19 届 IFAC 世界大会上。 (基本效应是通过对拉丁超立方体中采样的径向点应用大变化来计算的) 为了降低低估和修复不可忽略因素的风险: http : //fr.mathworks.c

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

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

weixin_50202509的博客 1425

Windows Server下OnlyOffice Workspace社区版安装避坑指南(含局域网邮箱配置)

本文详细介绍了在Windows Server环境下部署OnlyOffice Workspace社区版的完整流程,特别针对企业内网环境中的邮箱配置和端口冲突等常见问题提供了实用解决方案。通过硬件资源配置建议、软件环境检查清单以及内网邮箱系统搭建方案对比,帮助用户高效搭建企业级协作平台,实现团队协作的无缝对接。

weixin_29231263的博客 449

morris算法(莫里斯遍历) [数据结构与算法]

Morris算法, 我们最常用于解决二叉树的遍历问题, 以及与遍历相关的一些问题(其实在二叉树系列问题中, 我们解决各种问题的时候都要涉及到一个对二叉树的遍历)使用递归解决二叉树的遍历本质其实很简单, 我们只需要编写一个递归方法, 在其中先进行向左遍历, 遍历到最后之后开始向右遍历, 这样我们对于我们递归到的结点就会有一个打印时机的区分:(如下: )一个节点(包括叶子结点), 肯定是会经过三次, 第一次就是向左遍历或者向右遍历的过程中, 第二次和第三次就是向左和向右遍历完成之后回溯的时候各会遍历到一次, 而

m0_57001006的博客 1339

CKAD-CN 考试知识点分享(12) Secret - 1

步骤操作完成1提取 Deployment 中的硬编码 Secret 值✅2创建名为postgres的 Secret✅3修改 Deployment 使用引用 Secret✅4验证新 Pod 正常运行✅❌未删除原 Deployment✅(遵守要求)✅任务完成。敏感信息已从环境变量移出,安全地存储在 Kubernetes Secret 中。

轻的博客 632

HFSS 3D Layout一键导入PCB,高效搞定微带线损耗仿真

在高速PCB设计中,信号完整性与电磁仿真是确保链路质量的关键环节。传统的HFSS手动建模流程繁琐且极易出错,尤其面对微带线、带状线等高频结构时,叠层参数、铜箔粗糙度等细节难以精准还原,导致仿真结果与实测偏差较大。HFSS 3D Layout模块提供了从EDA工具直接导入PCB文件的能力,完整保留走线、过孔、叠层及材料属性,结合精确的介质损耗模型与端口设置,大幅缩短建模时间并提升仿真精度。该技术广泛应用于DDR、SerDes等高速数字链路的设计验证,以及SI/PI工程师对PCB损耗性能的快速评估。本文从导入操

weixin_29191081的博客 256

【MATLAB第107期】基于MATLAB的Morris局部敏感性分析模型(无目标函数)

Morris方法采用概率均匀抽样的方式估计每个模型输入因子在输出结果中的重要性,通过比较系统在不同输入参数值上的输出结果变化来度量每个参数的敏感性。该方法的原理与局部敏感性分析中的“一次变化一个因子”方法相似,通过计算所谓的基本效应(elementary effect, EE)来衡量每个参数对模型输出的全序影响。如式(1)所示, 对初始状态进行多次反复采样, 求各次采样的基效应,并用式(2)求其均值 μi, 衡量每个参数对模型输出的全序影响。

随风飘摇的土木狗的博客 4633

告别环境配置噩梦:用Docker Desktop + WSL2在Windows上5分钟搞定vLLM运行环境

本文详细介绍了如何在Windows系统上使用Docker Desktop和WSL2快速搭建vLLM开发环境,5分钟内完成配置。通过容器化技术解决传统环境配置中的依赖冲突、环境污染等问题,提供一键复用的高效方案,特别适合AI开发者快速部署和测试vLLM框架。

weixin_28728495的博客 503

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

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

qq_46209845的博客 7204

408王道计算机组成原理强化——指令系统及大题解构

1.大纲要求 2.x86

JiangNan_1002的博客 8512

INCA基础教程

以软件自带Demo为例记录INCA学习过程,主要内容包括测量与标定。

yunqianrui的博客 6万+

Morris算法的理解与实现

本文介绍了Morris遍历算法,一种空间复杂度为O(1)的二叉树遍历方法。相比递归和栈实现的O(h)空间复杂度,Morris算法通过临时修改树结构(让左子树最右节点指向当前节点)实现遍历,随后恢复原结构。文章详细分析了Morris中序遍历的代码实现过程,通过实例演示了指针变化和访问顺序,并解释了算法难点。此外,还展示了如何调整访问时机将算法改为前序遍历。该算法体现了用空间换时间的思想,虽然路径更长但保持了线性时间复杂度,是一种巧妙的遍历优化方案。

m0_51150296的博客 1612

Matlab_Marr小波尺度变换图

多尺度的连续小波变换分解后时间尺度图 墨西哥Marr小波函数: %delta 小波变换尺度 %N     小波函数的长度 %s     原始信号 %g     原始信号某个尺度下的小波变化系数 function g=Singularity_Detection(delta,N,s); %保持信号长度 n=length(s); % 构造墨西哥小帽小波函数 for index_x=1

桔色小西瓜 6602

神级遍历——Morris算法及其应用

Morris   二叉树(Binary tree)是树形结构的一个重要类型。提到二叉树,我们首先想到的就是其3种遍历方式–前序、中序和后序,对于这三种遍历方式,我们很容易通过使用递归或者迭代的方式实现,时间复杂度为O(N)。但是这两种实现方式都需要使用堆栈进行节点信息的存储,即空间复杂度也是O(N)。   但是还有一种更为巧妙的遍历方法–Morris算法,该算法的时间复杂度也是O(N),但是空间复杂度却能达到最优的O(1) Morris遍历的实现:   记当前节点 cur ,一开始 cur 指向整棵树的头节

qq_45491549的博客 1640

基于YOLO26深度学习框架的人脸识别门禁系统的算法研究实现方法

本文研究了基于YOLO26深度学习框架的人脸识别门禁系统实现方法。系统采用YOLO26目标检测算法,通过CSPDarknet53主干网络、特征金字塔结构和注意力机制实现高效人脸检测。研究内容包括数据预处理、模型训练优化和身份验证等关键技术,使用迁移学习策略和CIoU损失函数提升检测精度。该系统可应用于智能安防场景,具有实时性强、识别准确的特点,为门禁管理提供了智能化解决方案。

一键难忘的博客 766

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

C语言——逆转链表(有、无头节点)、头插链表、尾插链表

【代码】C语言——逆转链表(有、无头节点)、头插链表、尾插链表。

白井的博客 923

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

二叉树奇技:Morris中序遍历

Morris遍历在中序遍历的过程中添加了指向,类似于手算二叉树的过程,是一种空间换时间的巧妙方法。

m0_74787564的博客 1358

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

算法2:链表的逆转

算法系列之链表

街头小瘪三的专栏 903

C++Morris遍历

Morris算法是针对二叉树实现的一个遍历算法,它是一种空间复杂度为O(1)的遍历算法通常情况下使用迭代或递归的方式遍历二叉树的空间开销都是O(N)级别的,较为理想的情况下可以做到O(logn)级别,而Morris算法通过更改节点指针指向的方式做到了它们都做不到的事情,可谓非常厉害。

KamikazePilot的博客 601

【算法&数据结构体系篇class30】:Morris遍历

1)如果cur没有左孩子,cur向右移动(cur = cur.right)一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1)b.如果mostRight的右指针指向cur,让其指向null,a.如果mostRight的右指针指向空,让其指向cur,然后cur向右移动(cur = cur.right)然后cur向左移动(cur = cur.left)通过利用原树中大量空闲指针的方式,达到节省空间的目的。假设来到当前节点cur,开始时cur来到头节点位置。为头的树中,最小深度是多少?

studyday1的博客 596

图解Morris遍历

Morris遍历实现

bdn_nbd的博客 147

Morris遍历

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

2301_79985750的博客 1232

一文详解反转二叉树

反转二叉树

abc123mma的博客 1226

【算法笔记】反转二叉树

反转二叉树问题 翻转一棵二叉树。 示例: 输入: 输出: 问题分析

weiwai的博客 3608

二叉树反转java实现

反转二叉树是数据结构中一种经典的操作。如下图所以,反转二叉树就是交换所有节点的左右子树。 具体代码实现如下:package com.xiaomi.tree;import java.util.LinkedList; import java.util.List;/** * Created by wanglei on 17/8/5. */ public class Solution { pri

bitcarmanlee的博客 4278

反转二叉树

反转二叉树,因为Max Howell(Homebrew,mac版的apt-get作者 )出名: Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off.在leetcode上找了这道题的解答1.递

bitcarmanlee的博客 5681
上一篇: 基于 double array 实现汉字的trie树索引 与 查询功能 python实现
下一篇: ac自动机
stund
博客等级 码龄18年 28粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值