Moriis神级遍历!
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神级遍历!的更多相关文章
- 《程序员代码面试指南》第三章 二叉树问题 遍历二叉树的神级方法 morris
题目 遍历二叉树的神级方法 morris java代码 package com.lizhouwei.chapter3; /** * @Description:遍历二叉树的神级方法 morris * @ ...
- VIM自动补全插件 - YouCompleteMe--"大神级vim补全插件"
VIM自动补全插件 - YouCompleteMe 序言 vim 之所以被称为编辑器之神多半归功于其丰富的可DIY的灵活插件功能,( 例如vim下的这款神级般的代码补全插件YouCompleteMe) ...
- Github欢乐多 PHP神级代码引发吐槽热
前日,github的PHP板块惊现一段能够提升70%运行效率的代码,引发了全世界众多网友的吐槽和调侃,“awesome!”.“well done!”.“PHP是世界第一语言!”平时不苟言笑,埋头苦干的 ...
- 用了TextMate才知道什么叫神级Editor
用了TextMate才知道什么叫神级Editor 一直用Eclipse作为开发Ruby和Java项目的IDE,但是太耗内存,再开个Firefox和虚拟机就可以直接将MBP弄残了..看到大家都对Mac下 ...
- 20171201 - macOS High Sierra 神级 bug
昨日亲测有效,macOS High Sierra 神级 bug,系统管理员 root 密码为空,输入就可以登录,具备最高权限. 让人不禁想象 Apple Software 怎么了,人才都流失了吗?
- 大神级回答exists与in的区别
google搜了一下,很多帖子,而且出发点不同,各有各的道理,但是有一个帖子讲的特别好: http://zhidao.baidu.com/question/134174568.html 忍不住在百度上 ...
- IntelliJ IDEA 15款 神级超级牛逼插件推荐(超赞,谁用谁知道)
满满的都是干货 所有插件都是在 ctrl+alt+s 里的plugins 里进行搜索安装 1.CodeGlance 代码迷你缩放图插件 2. Codota 代码提示工具,扫描你的代码后,根据你的敲击 ...
- 左神算法书籍《程序员代码面试指南》——3_05Morris遍历二叉树的神级方法【★★★★★】
[问题]介绍一种时间复杂度O(N),额外空间复杂度O(1)的二叉树的遍历方式,N为二叉树的节点个数无论是递归还是非递归,避免不了额外空间为O(h),h 为二叉树的高度使用morris遍历,即利用空节点 ...
- linux内核神级list
源码: #ifndef _LINUX_LIST_H #define _LINUX_LIST_H /* * Simple doubly linked list implementation. * * S ...
随机推荐
- mybatis plus @TableId注解 type属性的含义
首先该注解用在主键id上,它的type属性有8种类型 AUTO(0),NONE(1),INPUT(2),ASSIGN_ID(3),ASSIGN_UUID(4),ID_WORKER(3),ID_WORK ...
- 高精度加法(C++实现)
高精度加法 简介 用于计算含有超过一般变量存放不下的非负整数 高精度加法这个过程是模拟的小学竖式加法计算 步骤 以下有顺序之分 数组清零 输入 获取长度 逆置 字符型数字转成对应的整型数字 计算并输出 ...
- zabbix监控SSL证书有效期
想给公司网站加上证书的监控,发现agent无此监控项.科普之后发现需要自行添加脚本以及一些操作. 环境信息 系统版本: Ubuntu20.04 zabbix server版本:5.4 (这个自定义貌似 ...
- Blazor使用区域(Area)
在MVC中,我们经常使用区域(Area)来区分各个模块,比如后台我们可以写一个Admin的Area. 到了Blazor时代,已经不推荐这么做了,现在推荐的做法是通过Url来区分,比如Admin可以配置 ...
- 用 getElementsByTagName() 来获取,父元素指定的子元素
1. html 结构 <ul> <li>知否知否,应是等你好久11</li> <li>知否知否,应是等你好久11</li> <li&g ...
- Android四大组件——Activity——Activity数据回传
既然可以传递数据给下一个Activity,自然也可以返回数据给上一个Activity.返回上一个Activity时只需要点击back键就好,并没有一个用于启动Activity的Intent来传递数据, ...
- 记一次jenkins发送邮件报错 一直报错 Could not send email as a part of the post-build publishers问题
写在前面 虽然Jenkins是开源.免费的,好处很多,但有些功能上的使用,我个人还是很不喜欢,感觉用起来特别麻烦.繁琐. 为什么? 就拿这个邮件配置来说吧,因重装系统,电脑需要配置很多东西,结果今天就 ...
- JS获取Cookie失败
项目开发日记-bug多多篇(1) 在做评论功能的时候遇到了一个很无厘头的错误,我的思路是参照点赞功能,用Ajax技术异步完成评论信息的传输,然后展示在页面上. 那么在提交评论信息的同时,要连着用户名, ...
- 基于Ansible实现Apache Doris快速部署运维指南
Doris Ansible 使用指南 Apache Doris 介绍 Apache Doris是一个现代化的MPP分析型数据库产品.仅需亚秒级响应时间即可获得查询结果,有效地支持实时数据分析.Apac ...
- Tensorflow 窗口时间序列数据的处理
Tensorflow 时间序列数据的处理 数据集简介 数据来源:Kaggle Ubiquant Market Prediction 数据集描述了多个投资项目在一个时间序列下的300个匿名特征(&quo ...