二叉树的Morris遍历

news2024/10/3 21:17:19

        Morris 遍历的是指就是避免用栈结构,而是让下层到上层有指针,具体时通过让底层节点指向 null 的空闲指针指回上层的某个节点,从而完成下层到上层的移动。

Morris 遍历的过程:

假设当前节点为cur,初始时cur就是整棵树的头节点,根据以下标准让cur移动:

1. 如果 cur 为 null,则过程停止,否则继续下面的过程。

2. 如果 cur 没有左子树,则让cur向右移动,即令 cur = cur.right。

3. 如果 cur 有左子树,则找到cur左子树上最右的节点,记为 mostRight。

1)如果 mostRight 的right指针指向null,则令 mostRight.right = cur,也就是让mostRight 的right指针指向当前节点,然后让cur向左移动,即令 cur = cur.left。

2)如果 mostRight 的right指针指向cur,则令 mostRight.right = null,也就是让 mostRight的right指针指向null,然后让cur向右移动,即令cur = cur.right。

举例:

 

 

 Morris 序代码实现:

    public static void morris(Node head) {
        if (head == null) {
            return;
        }
        Node cur = head;
        Node mostRight = null;
        while (cur != null) {
            mostRight = cur.left;
            //如果当前cur有左子树
            if (mostRight != null) {
                //找到左子树上最右的节点
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                //上面的while结束后,mostRight就是最右的节点
                if (mostRight.right == null) {
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;//回到最外层的while,继续判断cur的情况
                } else {
                    //如果mostRight指向cur 让其指回null
                    mostRight.right = null;
                }
            } 
            //cur如果没有左子树,cur向右移动
            //或者cur左子树的最右节点的右指针是指向cur的,cur向右移动
            cur = cur.right;
        }
    }

根据 Morris 遍历,加工出先序遍历:

1. 对于cur只能到达一次的节点(无左子树的节点),cur到达时直接打印

2. 对于cur可以到达两次的节点(有左子树的节点),cur第一次到达时打印,第二次到达时不打印。

先序遍历:

    public static void morrisPre(Node head) {
        if (head == null) {
            return;
        }
        Node cur = head;
        Node mostRight = null;
        while (cur != null) {
            mostRight = cur.left;
            //这里是有左子树的情况
            if (mostRight != null) {
                //这个while循环就是找 mostRight节点
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                //当mostRight 为空时,说明第一次到达这个节点 直接打印。
                if (mostRight.right == null) {
                    mostRight.right = cur;
                    System.out.print(cur.value + " ");
                    cur = cur.left;
                    continue;
                //这里说明第二次到达这个节点 在先序中不在此处打印
                } else {
                    mostRight.right = null;
                }
            //这里就是没有左子树的情况 直接打印
            } else {
                System.out.print(cur.value + " ");
            }
            cur = cur.right;
        }
    }

根据 Morris 遍历,加工出中序遍历:

1. 对于cur只能到达一次的节点(无左子树的节点),cur到达时直接打印

2. 对于cur可以到达两次的节点(有左子树的节点),cur第一次到达时不打印,第二次到达时打印。

中序遍历: 

    public static void morrisIn(Node head) {
        if (head == null) {
            return;
        }
        Node cur = head;
        Node mostRight = null;
        while (cur != null) {
            mostRight = cur.left;
            //这里是有左子树的情况
            if (mostRight != null) {
                //这个while循环就是找 mostRight节点
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                //当mostRight 为空时,说明第一次到达这个节点 中序中不打印。
                if (mostRight.right == null) {
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                    //这里说明第二次到达这个节点 在中序中此处需要打印
                } else {
                    System.out.print(cur.value + " ");
                    mostRight.right = null;
                }
                //这里就是没有左子树的情况 直接打印
            } else {
                System.out.print(cur.value + " ");
            }
            cur = cur.right;
        }
    }

测试结果:

        Node head = new Node(1);
        Node node1 = new Node(2);
        Node node2 = new Node(3);
        Node node3 = new Node(4);
        Node node4 = new Node(5);
        Node node5 = new Node(6);

        head.left = node1;
        head.right = node2;
        node1.left = node3;
        node1.right = node4;
        node2.right = node5;

        morrisPre(head);
        System.out.println();
        morrisIn(head);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

33-剑指 Offer 34. 二叉树中和为某一值的路径

题目给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有从根节点到叶子节点路径总和等于给定目标和的路径。叶子节点是指没有子节点的节点。示例 1:输入:root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22输出&#…

邮箱中的Qt线程设计

邮箱(deepin-mail)主要使用Qt框架开发,是一个有大量并行任务且需要监控进度和结果的项目,任务的优先级调整和支持取消回滚也是必不可少。Qt已经为我们提供了多种线程设计的方法,可以满足绝大部分使用场景,但…

钧瓷产业基础架构(SCA架构)是钧瓷产业发展的核心引擎

《钧瓷内参》独立客观,原创前瞻,深度评论,定期推送 钧 瓷 内 参 第3期(总第335期) 2023年1月3日 钧瓷产业数字化发展的下一个阶段——钧瓷共同体,是钧瓷产业数字化发展的必然趋势。 实现钧瓷共同体的路线…

JavaSE从基础到入门:String类的学习

前言 字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。 1.String类的方法 1.字符串的构造方法 使用常量串构造 String s1 "hello world"; System.out.println(s1);直接newString对象…

【Linux】Linux基本权限

作者:一个喜欢猫咪的的程序员 专栏:《Linux》 喜欢的话:世间因为少年的挺身而出,而更加瑰丽。 ——《人民日报》 目录 1.shell命令以及运行原理 2.Linux权限 2.1Linux权限的概念 2.2Linux上用户…

DALLE2-文本图像生成

文章目录摘要算法解码器prior图像处理变体插值文本差异限制论文: 《Hierarchical Text-Conditional Image Generation with CLIP Latents》github: https://github.com/lucidrains/DALLE2-pytorchhttps://github.com/LAION-AI/dalle2-laion摘要 CLIP已经…

window环境安装mysql8.0.12版本的安装、配置(详细步骤图解)

目录一、mysql官网下载网址二、下载步骤三、安装步骤四、测试链接一、mysql官网下载网址 mysql官网下载网址 https://www.mysql.com/ 二、下载步骤 浏览器输入https://www.mysql.com/网址,点击【DownLoads】,如下图: 向下滑动网页&#x…

软件测试面试注意事项汇总

面对最近的复工热潮,不少求职者也开始蠢蠢欲动准备找工作了。相信大家都知道疫情下面试求职的压力是有史以来最大的,我们唯一能做好的只有积极的准备面试,让自己可以更加从容的面对的面试官的提问。下面小编为大家汇总了软件测试面试过程中的…

CSDN官方猿如意工具体验

2022年注定是不平凡的一年,2022再见,2023你好! 2023愿我们发财,被爱,一路好运常在!愿所念之人平安喜乐,所想之事顺心如意,岁岁常欢喜,万事皆胜意! 猿如意工具…

中间件:Win10安装运行Kafka

一、JDK环境安装配置 可参考:百度安全验证 二、Zookeeper安装配置 1、下载 : Index of /dist/zookeeper/zookeeper-3.4.9 2、解压到本地,目录不要带中文符号,保证纯英文 3、zoo.cfg修改 4、cmd运行,使用命令zkServe…

ConcurrentHashMap 线程安全

JDK1.7 结构 数据结构是数组segment对象,采用segment分段锁和CAS保证并发。 加锁 JDK1.7中的ConcurrentHashMap是由 segment数组结构和 HashEntry 数组结构组成,即 ConcurrentHashMap把哈希桶切分成小数组(Segment ),每个Segment 有n个 Hash…

终于!Linaro 加盟 Zephyr 项目

导读为物联网构建实时操作系统的开源协作项目 Zephyr 项目宣布,Linaro 有限责任公司以白金会员的身份加盟该项目。Linaro是一家为 ARM 架构开发开源软件的协作工程组织,也是全球性机构,其 35 个成员中不乏来自多个行业部门的龙头企业。Linaro…

嵌入式实时操作系统的设计与开发(一)

以一款简单、易学的嵌入式开发平台ARM Mini2440(CPU是三星ARM 9系列的ARM S3C2440)为例,通过具体代码实现,介绍如何从裸板入手设计简单的轮询系统、前后台系统,以及如何一步一步在ARM Mini2440上编写RTOS内核&#xff…

Spring之配置非自定义Bean

目录 一:概述 二:代码演示 1)配置Druid数据源交由Spring管理 一:概述 以上在xml中配置的Bean都是自己定义的, 例如:UserDaolmpl, UserServicelmpl。但是, 在实际开发中有些 功能类并不是我们…

包装器和绑定器std::bind和std::function的回调技术

回调函数 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在…

低代码搭建门店管理之收发货管理系统

随着电商的深入,不少门店都开始采用线上模式进行销售了,并且有的门店线上销售更是比线下销售更加火爆。因此,线上销售务必会涉及到收发货这个步骤。以前线上销售刚兴起的时候收发货只靠人工纸质化登记就能搞定,但是随着线上销售的…

盘点这些年稚晖君的DIY项目,看看他的技术栈有多强

近日,知名极客稚晖君在个人微博发文称自己将离职创业,开启一段新的旅程,“天才少年”将在机器人领域继续发光发热。 自2020年初发布第一个出圈视频《技术宅UP耗时三个月,自制B站最强小电视!》以来,稚晖君共…

Vue实例的基本属性,computed计算属性,watch监听属性以及过滤器filters

目录 一、Vue实例的属性 二、Vue实例的计算属性:computed。计算属性结果会被缓存起来,当依赖的响应式属性发生变化时,才会重新计算,返回最终结果。 三、Vue实例的状态监听属性:watch,可以对元素的值的变…

JVM垃圾回收相关算法-垃圾标记阶段

文章目录学习资料垃圾回收概念概述垃圾回收相关算法垃圾标记阶段:对象存活判断引用计数算法可达性分析算法(或根搜索算法、追踪性垃圾收集)【Java使用算法】基本思路GC Roots对象的finalization机制对象处于三种可能的状态具体过程学习资料 …

WebDAV之葫芦儿·派盘+WebDAV Nav Lite

WebDAV Nav Lite 支持WebDAV方式连接葫芦儿派盘。 支持连接所有WebDAV服务器、云存储、NAS设备的管理工具,并可以直接管理设备内的文件?那快来试下WebDAV Nav Lite自动同步与管理工具吧。 WebDAV Nav Lite允许您