Morris遍历(值得学习的二叉树遍历方法)

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

Morris遍历

morris遍历是二叉树遍历算法的超强进阶算法,跟递归、非递归(栈实现)的空间复杂度,
morris遍历可以将非递归遍历中的空间复杂度降为O(1)。从而实现时间复杂度为O(N),而空间复杂度为O(1)的精妙算法。
morris遍历利用的是树的叶节点左右孩子为空(树的大量空闲指针),实现空间开销的极限缩减。

Morris遍历的实现原则

记作当前节点为cur。

  1. 如果cur无左孩子,cur向右移动(cur=cur.right)

  2. 如果cur有左孩子,找到cur左子树上最右的节点,记为mostright

    1. 如果mostright的right指针指向空,让其指向cur,cur向左移动(cur=cur.left)
    2. 如果mostright的right指针指向cur,让其指向空,cur向右移动(cur=cur.right)

实现以上的原则,即实现了morris遍历。

Morris遍历的实现原则

建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次

morris遍历的实例

一个树若按层遍历的结构为{1,2,3,4,5,6,7},即该树为满二叉树,头结点值为1,左右孩子为2,3,叶节点为4,5,6,7

一开始图示:
在这里插入图片描述
我们按照morris遍历来遍历该树。
1)首先cur来到头结点1,按照morris原则的第二条第一点,它存在左孩子,cur左子树上最右的节点为5,它的right指针指向空,所以让其指向1,cur向左移动到2。

2)2有左孩子,且它左子树最右的节点4指向空,按照morris原则的第二条第一点,让4的right指针指向2,cur向左移动到4

3)4不存在左孩子,按照morris原则的第一条,cur向右移动,在第二步中,4的right指针已经指向了2,所以cur会回到2

4)重新回到2,有左孩子,它左子树最右的节点为4,但是在第二步中,4的right指针已经指向了2,不为空。所以按照morris原则的第二条第二点,2向右移动到5,同时4的right指针重新指向空

5)5不存在左孩子,按照morris原则的第一条,cur向右移动,在第一步中,5的right指针已经指向了1,所以cur会回到1

6)cur回到1,回到头结点,左子树遍历完成,1有左孩子,左子树上最右的节点为5,它的right指针指向1,按照morris原则的第二条第二点,1向右移动到3,同时5的right指针重新指回空

以上内容转载于:https://zhuanlan.zhihu.com/p/101321696

代码实现(Java)

/**
 * Morri 遍历
 * 	二叉树的前序、中序、后序遍历
 *	目的:遍历树的每一个节点,使用叶子节点大量空指针的特点,将叶子节点空子树指向它的后继节点
 */
package com.wsq.leetcode;

public class TraversingBinaryTrees {
	/**
	 * Morri二叉树先序遍历
	 * @param head
	 */
	public static void morrisPre(TreeNode head) {
		if(head == null) {
			return;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		
		while(cur != null) {
			if(cur.left == null) {
				System.out.print(cur.val + " ");
				cur = cur.right;
			}else {
				mostRight = getMostRight(cur);
				if(mostRight.right == null) {
					mostRight.right = cur;
					System.out.print(cur.val + " ");
					cur = cur.left;
				}else {
					mostRight.right = null;
//					System.out.print(cur.val);
					cur = cur.right;
				}
			}
		}
		System.out.println();
	}
	/**
	 * Morri中序遍历
	 * @param head
	 */
	public static void morrisIn(TreeNode head) {
		if(head == null) {
			return;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		
		while(cur != null) {
			if(cur.left == null) {
				System.out.print(cur.val + " ");
				cur = cur.right;
			}else {
				mostRight = getMostRight(cur);
				if(mostRight.right == null) {
					mostRight.right = cur;
					cur = cur.left;
				}else {
					mostRight.right = null;
					System.out.print(cur.val + " ");
					cur = cur.right;
				}
			}
		}
		System.out.println();
	}
	/**
	 * Morri 遍历 后序遍历
	 * @param head
	 */
	public static void morrisPos(TreeNode head) {
		if(head == null) {
			return;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		
		while(cur != null) {
			if(cur.left == null) {
				cur = cur.right;
			}else {
				mostRight = getMostRight(cur);
				if(mostRight.right == null) {
					mostRight.right = cur;
					cur = cur.left;
				}else {
					// 当节点第二次别访问,则打印左子树最右分支路径边
					mostRight.right = null;
					printEdge(cur.left);
					cur = cur.right;
				}
			}
		
		}
		printEdge(head);
		System.out.println();
	}
	
	public static void printEdge(TreeNode node) {
		// 类似翻转链表
		TreeNode tail = reverseEdge(node);
		TreeNode cur = tail; 
		while(cur != null) {
			System.out.print(cur.val + " ");
			cur = cur.right;
		}
		// 恢复原来的形状
		reverseEdge(tail);
	}
	/**
	 *	翻转左子树的最右的边缘
	 *	类似链表的翻转
	 * @param node
	 * @return
	 */
	public static TreeNode reverseEdge(TreeNode node) {
		TreeNode pre = null;
		TreeNode next = null;
		while(node != null) {
			next = node.right;
			node.right = pre;
			pre = node;
			node = next;
		}
		return pre;
	}
	/**
	 *	获取后继节点为"当前节点"的节点
	 *	
	 * @param cur
	 * @return
	 */
	private static TreeNode getMostRight(TreeNode cur) {
		// TODO Auto-generated method stub
		TreeNode mostRight = cur.left;
		while(mostRight.right != null && mostRight.right != cur) {
			mostRight = mostRight.right;
		}
		return mostRight;
	}
	
	public static void main(String[] args) {
    	int[] a = {4,2,7,1,3,6,9};
    	TreeNode root = buildTree(a, 0);
    	System.out.println("前序遍历");
    	morrisPre(root);
    	System.out.println("中序遍历");
    	morrisIn(root);
    	System.out.println("后序遍历");
    	morrisPos(root);
	}
    
    public static TreeNode buildTree(int[] a, int pos) {
    	if(pos >= a.length) {
    		return null;
    	}
    	TreeNode root = new TreeNode(a[pos]);
    	root.left = buildTree(a, 2 * pos + 1);
    	root.right = buildTree(a, 2 * pos + 2);
    	return root;
    }
}

Pytorch学习笔记(III)——提取特征 目录一、引言二、具体步骤1、参数模型2、网络结构3、参数载入4、特征提取器5、读取图片三、完整代码 一、引言   深度学习在许多任务中主要充当着特征学习的作用,而学习完的特征才是后续应用的一个关键。本文将主要介绍,如何提取任意目标层的特征图。   本文以输入数据为图片为例。 二、具体步骤 1、参数模型 博主使用了ResNet50训练了一个人脸识别的网络   训练完成的深度学习模型,我们会保存一... 阅读详情

相关推荐

ENVI5.6 自动化流程:GF系列卫星影像批处理与质量优化

本文详细介绍了如何利用ENVI5.6构建针对GF-2、GF-6、GF-7系列卫星影像的自动化批处理流水线。通过配置Batch Processing工具,实现从正射校正到图像融合的全流程批量处理,并分享参数设置、质量监控与故障排查的实战经验,旨在显著提升海量遥感影像的处理效率与成果质量。

efc1234567的博客 604

Morris遍历

时间复杂度O(N),空间复杂度O(1)的Morris遍历(二叉树的遍历)的讲解与实现,以及Morris遍历如何加工出先序、中序和后序

明朗晨光的专栏 1660

从Google到LLVM:5种主流clang-format预设风格横向对比(含代码对比图)

本文横向对比了LLVM、Google、Chromium、Mozilla和WebKit五种主流的clang-format预设代码风格。通过同一段C++代码的格式化效果对比图,直观展示了它们在指针对齐、大括号换行、缩进等关键细节上的差异,并提供了基于团队背景与项目基因的选型决策指南。

weixin_28828243的博客 445

树的前中后序的Morris遍历

Morris遍历是一种用于二叉树遍历的算法,它可以在不使用栈或队列的情况下实现中序遍历。该算法的时间复杂度为O(n)空间复杂度为O(1)。前序和中序的遍历很想,只不过在打印(收集结点信息的时候不同),中序遍历是在当前结点的左节点为空(curr.left==null),或者当前结点已经被线索化(prev.right==curr)的时候进行打印,仔细观察前序遍历的过程,我们通过修改打印的顺序即可.前序遍历是在当前结点的左节点为空(curr.left==null),或者当前结点没有被线索化(prev.right=

qq_64580912的博客 2470

ScanNet数据集下载全攻略:从申请到解析的保姆级教程(含常见问题解决)

本文提供了ScanNet数据集从申请授权到下载、解析的完整实战指南。针对这个大规模室内RGB-D数据集,详细介绍了官方申请流程、优化下载策略、数据验证方法,并重点解决了深度图解析等常见问题,帮助三维视觉研究者高效获取和使用这一宝贵资源。

5f4d3s2a1q的博客 622

蓝桥杯算法:打印图形——六芒星

【输入格式】 输入一行包含一个整数 n。 【输出格式】 输出一个六芒星,表示满足题意的图形。 【样例输入】 3 4 【样例输出】 【评测用例规模与约定】 对于所有评测用例,1 ≤ n ≤ 20。 总结: 第一次做图形题,做了蛮久,图形题有很多的方式去完成,它考验的是你能不能找到更简单的那一种方式. 我初看到这个图形,第一种想法是把这个六芒星当做六个小三角形来做,然后以某种规则排列一下,就能做出来,但是尝试了一下感觉太麻烦了。 然后我的第二种想法是,这个图形是对称图形,怎么能写出一半吗? 这样另一半想办

KeepCodeing 3840

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

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

qq_46209845的博客 7210

用deepseek部署全自动的机器人--bytebot

Bytebot是一款开源AI桌面代理,提供完整的虚拟桌面环境,支持跨应用任务处理、文件管理、文档处理等复杂工作流。其技术架构包含核心控制、LLM代理、UI等组件,支持Docker和Kubernetes部署。本文演示了基于CloudStudio的部署流程,包括环境配置、Docker部署及DeepSeek API集成,实现多模型调用功能。

Kin 2481

C++实现二叉树遍历全家桶!!(递归,层次,Morris)

二叉树遍历大合集!!!一篇文章,一网打尽!!!

gbl2005的博客 2578

避开PSRR仿真三大坑:用Cadence psspxf分析分频器时,这些设置错了白忙活

本文深入探讨了使用Cadence psspxf工具进行分频器PSRR仿真时常见的三大技术陷阱,包括扰动信号参数设置、收敛性优化和后处理信号提取。通过详细的参数推荐和实战技巧,帮助工程师避免仿真误差,提升PSRR分析效率与准确性,特别适用于16分频器等数字-模拟混合电路设计。

weixin_29258297的博客 302

保姆级教程:在Windows/Mac上搞定SAM 3的权重和BPE文件下载与配置

本文提供了一份详细的SAM 3权重和BPE文件下载与配置指南,特别针对国内开发者遇到的下载缓慢和配置错误问题。通过国内镜像源快速获取文件,并详细介绍了环境检查、文件下载、路径配置及常见问题解决方案,帮助开发者高效完成SAM 3的部署与使用。

weixin_42547431的博客 552

Morris,我想让你遍历我的二叉树!

Morris算法是一种遍历二叉树的高效方法,在空间复杂度O(1)的情况下实现了先序遍历、中序遍历和后序遍历,并且时间复杂度为O(n)。相比于传统方法使用栈空间,Morris算法可以大大减少深度优先搜索时所需的空间。本篇博客在介绍Morris算法的过程中,特别以Python实现来控制代码的思路和执行方式,让读者更加易懂,在基本概念和实践操作之间切换,达到一定的学习效果。

2303_78983004的博客 855

2024年上半年试题二:论性能测试方法及其应用

2025年1月,我以系统分析师的身份参与了公司OA系统的开发与建设。该系统包含的功能有打卡签到、公文流转、工资查询等。我们吸取了之前由于系统使用人数激增暴露出的短时间卡顿等问题,所以在本次开发中,我们非常重视系统的性能测试。通过自动化的测试工具模拟多种正常、峰值及异常负载条件对系统的各项性能指标进行测试。性能测试在软件的质量保证中起着重要的作用,它包括的测试内容丰富多样,常见的负载测试和压力测试都属于性能测试。

银帅 548

morris算法实现二叉树遍历

morris算法实现二叉树的前中后序遍历

雨落成追忆 520

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

2024.4.8Morris中序遍历(线索二叉树)学习

既然递归中使用栈的原因是想记录前驱节点,那么我们只要达到记录前驱节点的目的即可不使用栈。首先,它的本质还是中序遍历。正常的中序遍历,在“递”左子树时是可以直接通过左指针到达,但是在“归”根节点的时候是没有办法直接到达的(右子树只用“递”,不用“归”)。而“归”的时候均是到达叶子节点才“归”,则可以充分利用叶子节点的左右空指针,将右空指针指向根节点,这样就可以直接“归”到根节点。其中,为什么是右空节点呢?

lshx4658的博客 926

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

【二叉树】Morris遍历——不需要辅助空间的遍历方法

Morris 遍历是一种节省空间复杂度的方法。将叶子节点上的空指针利用起来,指向父节点,当再次遍历到这个节点的时候再修改回来,这样最后二叉树的结构也没有发生改变。

雪野Solye的博客 362

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Leetcode145. Morris后序遍历

文章概览Morris后序遍历Morris后序遍历算法详解Leetcode 501题目描述解题思路完整代码 Morris后序遍历 前面我们遇到了Morris中序遍历算法,它可以实现O(1)的空间复杂度 我们结合Leetcode中第145题二叉树的后序遍历,来学习Morris后遍历算法。 以下使用python3~ Morris后序遍历算法详解 本部分参考leetcode145官方题解 在中序遍历的时候,一定先遍历左子树,然后遍历当前节点,最后遍历右子树。在常规方法中,我们用递归回溯或者是栈来保证遍历完左子树可以

lijianyi0219的博客 2709

Morris二叉树遍历算法

在遍历儿叉树时,常常使用的是递归遍历,或者是借助于栈来迭代,在遍历过程中,每个节点仅访问一次,所以这样遍历的时间复杂度为O(n),空间复杂度为O(n),并且递归的算法易于理解和实现,二叉树的递归遍历算法代码如下: 儿叉树的定义: typedef struct BNode { char ch; struct BNode *left, *right; } BNode, *BiTre

东方吹雪的专栏 7217

算法学习13: Morris遍历

Morris遍历 树常见的非递归遍历方法,其空间复杂度均为O(h)h为树的高度 Morris遍历做到了 使遍历过程的空间复杂度 真的降低为O(1) 其思路: 利用树的空闲的空间(如,叶节点指向的虽为null,但是在内存中依旧为其分配了空间) morries遍历步骤 ...

ncepu_Chen的博客 651

图解Morris遍历:从线索二叉树到前中后序的完整推导过程

本文通过图解方式详细解析了Morris遍历算法,这是一种仅需O(1)额外空间即可实现二叉树前序、中序和后序遍历的巧妙方法。文章从线索二叉树的灵感出发,逐步推导其核心思想与通用框架,并重点阐述了如何利用临时修改指针(而非递归或栈)来完成遍历,是理解高效空间算法和指针操作的绝佳教程。

purple的博客 663

数据结构与算法学习笔记——二叉树遍历(二)(Morris Traversal)

前面介绍了一般的二叉树遍历方法,包括递归和迭代。这两种方法的空间复杂度都是O(n)。 二叉树遍历的另外一种方法:Morris遍历二叉树 的空间复杂度为O(1),并且还不改变原本的树结构,时间复杂度依然为O(n)。Morris的核心是利用叶子节点的空闲右孩子指针返回到根节点。中序遍历最初Morris是为中序遍历二叉树提出的方法,在中序的基础上可实现先序和后续遍历,这里先整理中序遍历。 中序遍历的顺

Mars.的专栏 1471

Morris算法的理解与实现

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

m0_51150296的博客 1612

Morris 中序遍历

Morris本质就是线索二叉树,我们从leetcode中的一道easy题来看看如何使用Morris树求解 Morris 遍历算法是另一种遍历二叉树的方法,它能将非递归的中序遍历空间复杂度降为 O(1)。 Morris 遍历算法整体步骤如下(假设当前遍历到的节点为 x): 1.如果 x 无左孩子,先将 x 的值加入答案数组,再访问 x 的右孩子,即 x=x.right。 2.如果 x 有左孩子,则找到 x 左子树上最右的节点(即左子树中序遍历的最后一个节点,x 在中序遍...

qq_56443561的博客 1230

二叉树中序遍历 Morris

-

夜空像是一片紫水潭,星星是些不动的大亮点,夜风是些浅蓝色的流线,云端传来喧嚣的声音。 561

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

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

weixin_50202509的博客 1425

【数据结构】C++对二叉树进行中序遍历(递归、迭代及Morris)

学习目标:使用c++对二叉树进行递归、迭代以及Morris的便利 文章目录递归迭代Morris代码实现运行结果参考 递归 二叉树的中序遍历:按照访问左子树——根节点——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历,直到遍历完整棵树。 思路: 迭代 Morris 代码实现 #include <iostream> #include <vector> #include <stack> using namespace std; usi.

qq_44747572的博客 3053
上一篇: 685. 冗余连接 II
下一篇: 11. 盛最多水的容器
Lazy mode
博客等级 码龄10年 13粉丝 311原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值