Moriis 遍历

Morris 遍历是二叉树遍历的一种方式,传统的递归和非递归遍历的时间复杂的都是O(N),空间复杂度都是O(h)(h为树的高度),而 Morris 遍历可以做到时间复杂的依然为 O(N) 的情况下,空间复杂度降到 O(1)。

Morris 遍历的实现原理

定义一个 cur 节点,指向 root 节点:

  • 当 cur.left == null 时,cur 向右移动 cur = cur.right

  • 当 cur.left != null 时,找到 cur 左子树的最右的节点 mostRight

    • 当 mostRight 右指针为空时,修改 mostRight 右指针指向 cur,cur 向左移动,cur = cur.left
    • 当 mostRight 右指针指向 cur 时,修改 mostRight 右指针指向 null,cur 向右移动
  • cur == null 停止。

cur 依次来到节点的顺序我们称之为 Moriis 序。我们来看下面的图片:

从 Morris 序中我们可以发现:

任何节点如果其有左孩子一定会在 Moriis 序中出现两次,为什么改动左子树最右节点的右指针的走向,为了确认是第一次来到当前节点还是第二次来到当前节点,左子树最右节点右指针指向空表示第一次来到当前节点,指向自己则说明第二次来。

Morris 遍历代码实现

我们观察上面示例的 Morris 序:1,2,4,2,5,1,3,6,3,7 可以发现:

拥有左子树的节点1,2,3都出现了两次,

所以我们看只打印第一次出现的节点,次序为1,2,4,5,3,6,7是不是发现这就是先序遍历了呢。

我们在尝试让其打印出现两次的节点的第二次,只出现一次的直接打印,顺序为4,2,5,1,6,3,7是不是发现这就是中序遍历了呢。

要实现后序遍历有一点繁琐,打印时机就在能回到自己两次且第二次回到自己的时候,但并非打印他自己 ,而是逆序打印它的左树右边界(二叉树的边界:左边界的定义是从根到最左侧结点的路径。 右边界的定义是从根到最右侧结点的路径。 若根没有左子树或右子树,则根自身就是左边界或右边界。),然后再单独逆序打印整棵树的右边界,来用上面的示例模拟一下过程:

  • 当第二次回到2时,此时 cur 的左树右边界为4,逆序打印4
  • 当第二次回到1时,此时 cur 的左树右边界为2 --> 5,逆序打印后4,5,2
  • 当第二次回到3时,此时 cur 的左树右边界为6,逆序打印后4,5,2,6
  • 最后单独逆序打印整棵树的右边界,打印后4,5,2,6,7,3,1为后序遍历的结果。

代码实现:

中序遍历:

public static void morrisIn(TreeNode root) {
if(root == null) {
return;
}
TreeNode cur = root;
TreeNode mostRight = null;
while(cur != null) {
mostRight = cur.left; //cur的左子树
if(mostRight != null) { //左子树不为空
//mostRight.right==null --> 找到左子树最右节点(第一次)
//mostRight.right != cur --> 找到左子树最右节点(第二次)
while(mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}//从while中出来就表示找到左子树最右节点为mostRight
if(mostRight.right == null) { //第一次到达
mostRight.right = cur; //修改mostRight右指针指向当前节点
cur = cur.left; //当前节点cur左移
continue;
} else { //mostRight.right == cur
mostRight.right = null; //当前节点第二次被访问,修改mostRight右指针指向null
}
}
System.out.println(cur.val + " "); //中序遍历
cur = cur.right; //cur.left == null(左子树为空) || mostRight.right != cur 此时都需要右移
}
}

先序遍历

public static void morrisPre(TreeNode root) {
if(root == null) {
return;
}
TreeNode cur = root;
TreeNode mostRight = null;
while(cur != null) {
mostRight = cur.left; //cur的左子树
if(mostRight != null) { //左子树不为空
//mostRight.right==null --> 找到左子树最右节点(第一次)
//mostRight.right != cur --> 找到左子树最右节点(第二次)
while(mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}//从while中出来就表示找到左子树最右节点为mostRight
if(mostRight.right == null) { //第一次到达
mostRight.right = cur; //修改mostRight右指针指向当前节点
System.out.println(cur.val); //先序遍历
cur = cur.left; //当前节点cur左移
continue;
} else { //mostRight.right != cur
mostRight.right = null; //当前节点第二次被访问,修改mostRight右指针指向null
}
}
else {
System.out.println(cur.val); //先序遍历
}
cur = cur.right; //cur.left == null(左子树为空) || mostRight.right != cur 此时都需要右移
}
}

后续遍历:

public static void morrisPos(TreeNode root) {
if(root == null){
return;
}
TreeNode cur = root;
TreeNode mostRight = null;
while (cur != null){
mostRight = cur.left;
if(mostRight != null){
while (mostRight.right !=null && mostRight.right != cur){
mostRight = mostRight.right;
}
if(mostRight.right == null){
mostRight.right = cur;
cur = cur.left;
continue;
}else {
mostRight.right = null;
printEdge(cur.left); //第二次出现,打印左树右边界
}
}
cur = cur.right;
}
printEdge(root); //打印整棵树的左树右边界
System.out.println();
}
public static void printEdge(TreeNode node){
TreeNode tail =reverseEdge(node);
TreeNode cur = tail;
while (cur != null ){
System.out.print(cur.value+" ");
cur =cur.right;
}
reverseEdge(tail);
}
public static TreeNode reverseEdge(TreeNode node){
Node pre = null;
Node next = null;
while (node != null){
next = node.right;
node.right = pre;
pre = node;
node = next;
}
return pre;
}

Moriis神级遍历!的更多相关文章

  1. 《程序员代码面试指南》第三章 二叉树问题 遍历二叉树的神级方法 morris

    题目 遍历二叉树的神级方法 morris java代码 package com.lizhouwei.chapter3; /** * @Description:遍历二叉树的神级方法 morris * @ ...

  2. VIM自动补全插件 - YouCompleteMe--"大神级vim补全插件"

    VIM自动补全插件 - YouCompleteMe 序言 vim 之所以被称为编辑器之神多半归功于其丰富的可DIY的灵活插件功能,( 例如vim下的这款神级般的代码补全插件YouCompleteMe) ...

  3. Github欢乐多 PHP神级代码引发吐槽热

    前日,github的PHP板块惊现一段能够提升70%运行效率的代码,引发了全世界众多网友的吐槽和调侃,“awesome!”.“well done!”.“PHP是世界第一语言!”平时不苟言笑,埋头苦干的 ...

  4. 用了TextMate才知道什么叫神级Editor

    用了TextMate才知道什么叫神级Editor 一直用Eclipse作为开发Ruby和Java项目的IDE,但是太耗内存,再开个Firefox和虚拟机就可以直接将MBP弄残了..看到大家都对Mac下 ...

  5. 20171201 - macOS High Sierra 神级 bug

    昨日亲测有效,macOS High Sierra 神级 bug,系统管理员 root 密码为空,输入就可以登录,具备最高权限. 让人不禁想象 Apple Software 怎么了,人才都流失了吗?

  6. 大神级回答exists与in的区别

    google搜了一下,很多帖子,而且出发点不同,各有各的道理,但是有一个帖子讲的特别好: http://zhidao.baidu.com/question/134174568.html 忍不住在百度上 ...

  7. IntelliJ IDEA 15款 神级超级牛逼插件推荐(超赞,谁用谁知道)

    满满的都是干货  所有插件都是在 ctrl+alt+s 里的plugins 里进行搜索安装 1.CodeGlance 代码迷你缩放图插件 2. Codota 代码提示工具,扫描你的代码后,根据你的敲击 ...

  8. 左神算法书籍《程序员代码面试指南》——3_05Morris遍历二叉树的神级方法【★★★★★】

    [问题]介绍一种时间复杂度O(N),额外空间复杂度O(1)的二叉树的遍历方式,N为二叉树的节点个数无论是递归还是非递归,避免不了额外空间为O(h),h 为二叉树的高度使用morris遍历,即利用空节点 ...

  9. linux内核神级list

    源码: #ifndef _LINUX_LIST_H #define _LINUX_LIST_H /* * Simple doubly linked list implementation. * * S ...

随机推荐

  1. 如何使用 python 爬取酷我在线音乐

    前言 写这篇博客的初衷是加深自己对网络请求发送和响应的理解,仅供学习使用,请勿用于非法用途!文明爬虫,从我做起.下面进入正题. 获取歌曲信息列表 在酷我的搜索框中输入关键词 aiko,回车之后可以看到 ...

  2. rabbitmq简单运用

    <?php /** * 生产者 */ $connection = new AMQPConnection([ 'host' => '192.168.23.130', 'port' => ...

  3. 牛客网 第十八届浙大城市学院程序设计竞赛(同步赛)J--万万没想到 啦啦啦啦啦

    我觉得我可以继续wa下去(手动魔鬼笑)--------------------------------------------   原题链接:https://ac.nowcoder.com/acm/c ...

  4. c#中判断类是否继承于泛型基类

    在c#中,有时候我们会编写类似这样的代码: public class a<T> { //具体类的实现 } public class b : a<string>{} 如果b继承a ...

  5. Transactional事务,事务嵌套的时候,如果主事务出现问题,子事务执行不需要回滚怎么做?

    如果调用的方法在不在同一个service当中,则只需要在子事务当中的方法上方添加注解即可 下方即是:这就话代表:重新开启一个新的事务 @Transactional(propagation = Prop ...

  6. 2021.12.06 P2508 [HAOI2008]圆上的整点(数论+ π )

    2021.12.06 P2508 [HAOI2008]圆上的整点(数论+ \(\pi\) ) https://www.luogu.com.cn/problem/P2508 题意: 求一个给定的圆 \( ...

  7. 代码管理工具-Git基础介绍及常用技巧

    目录 Git起源 基本概念 Branch.HEAD和Commit tree Git分支 git merge 和 git rebase 的区别和抉择 与远程仓库的交互 关于一些实际开发场景的问题和解决方 ...

  8. XCTF练习题---MISC---stage1

    XCTF练习题---MISC---stage1 flag:AlphaLab 解题步骤: 1.观察题目,下载附件 2.打开附件后发现是一张图片,初步判断是图片隐写,上Stegsolve进行转换,得到一张 ...

  9. ONNXRuntime学习笔记(四)

    接上一篇在Python端的onnx模型验证结果,上一篇在Pytorch和onnxruntime-gpu推理库上分别进行效果效率统计分析,结论要比最初设置的50ms高很多,这一篇我将在C++端写个测试代 ...

  10. Linux 系统安装 AutoFs 挂载服务

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 作者: Escape 链接: https://escapelife.github.io/pos ...