Morris遍历

通过利用空闲指针的方式,来节省空间。时间复杂度O(N),额外空间复杂度O(1)。普通的非递归和递归方法的额外空间和树的高度有关,递归的过程涉及到系统压栈,非递归需要自己申请栈空间,都具有O(N)的额外空间复杂度。

Morris遍历的原则:

1. 假设当前节点为cur,

2. 如果cur没有左孩子,cur向右移动,cur = cur.right

3. 如果cur有左孩子,找到左子树上最右的节点mostRight

  3.1 如果mostRight.right == null,令mostRight.right = cur,cur向左移动,cur = cur.left

  3.2 如果mostRight.right == cur,令mostRight.right = null,cur向右移动,cur = cur.right

4. 如果cur == null 停止遍历

Morris:1242513637

     public static void morris(TreeNode head){
if(head == null) return;
TreeNode cur = head;
TreeNode mostRight = null;
while(cur != null){//cur为空遍历停止【4】
mostRight = cur.left;//是cur左子树上最右的节点
if(mostRight != null){//有左子树【3】
while(mostRight.right != null && mostRight != cur){//mostRight!=cur不加就会绕环循环
mostRight = mostRight.right;//找最右节点【3】
}
if(mostRight.right == null){//第一次来到cur【3.1】
mostRight.right = cur;
cur = cur.left;
continue;//执行循环
}else {//mostRight.right = cur第二次来到cur【3.2】
mostRight.right = null;
}
}
cur = cur.right;//没有左子树【2】 }
}

所有节点遍历左子树右边界的时间总代价O(N)

基于Morris的先中后序遍历

如果cur有左子树一定能遍历2次,没有左子树只能遍历一次。

先序遍历

Morris:1242513637

Morris先序:1245367

基于Morris的先序遍历,如果一个节点可以到达两次则打印第一次,如果只能到达一次直接打印。

     public static void morrisPre(TreeNode head){
if(head == null) return;
TreeNode cur = head;
TreeNode mostRight = null;
while(cur != null){//有左子树
mostRight = cur.left;
if(mostRight != null){
while(mostRight.right != null && mostRight != cur){
mostRight = mostRight.right;
}
if(mostRight.right == null){//第一次来到左子树
System.out.println(cur.val);//打印
mostRight.right = cur;
cur = cur.left;
continue;
}else {
mostRight.right = null;
}
}else{
System.out.println(cur.val);//没有左子树 只会遍历一次
}
cur = cur.right;
}
}

中序遍历

Morris:1242513637

Morris中序:4251637

基于Morris的中序遍历,如果一个节点可以到达两次则打印第二次,如果只能到达一次直接打印。

     public static void morrisIn(TreeNode head) {
if(head == null) return;
TreeNode cur = head;
TreeNode mostRight = null;
while(cur != null){
mostRight = cur.left;
if(mostRight != null){
while(mostRight.right != null && mostRight != cur){
mostRight = mostRight.right;
}
if(mostRight.right == null){
mostRight.right = cur;
cur = cur.left;
continue;
}else {
mostRight.right = null;
}
}
//没有左树跳过if直接打印,有左树第二次来到cur退出循环打印
System.out.println(cur.val);
cur = cur.right;
}
}

后序遍历

Morris:1242513637

Morris先序:4526731

基于Morris的后序遍历,如果一个节点可以到达两次则第二次到达时逆序打印左树的右边界,单独逆序打印整棵树的右边界。

(1)逆序右边界(等同于单链表的逆序)

       public static TreeNode reverseEdge(TreeNode from) {
TreeNode pre = null;
TreeNode next = null;
while(from != null){
next = from.right;
from.right = pre;
pre = from;
from = next;
}
return pre;
}

(2)逆序打印以head为头节点的右边界。

     public static void printRightEdge(TreeNode head) {
TreeNode tail = reverseEdge(head);//逆转右边界
TreeNode cur = tail;
while(cur != null){
System.out.println(cur.val + " ");
cur = cur.right;
}
reverseEdge(tail);//逆转回去 恢复原树
}

(3)在Morris遍历中按时机打印。

     public static void morrisPost(TreeNode head){
if(head == null) return;
TreeNode cur = head;
TreeNode mostRight = null;
while(cur != null){
mostRight = cur.left;
if(mostRight != null){
while(mostRight.right != null && mostRight != cur){
mostRight = mostRight.right;
}
if(mostRight.right == null){
mostRight.right = cur;
cur = cur.left;
continue;
}else {//第二次达到 逆序打印左子树的右边界
mostRight.right = null;
printRightEdge(cur.left);
}
}
cur = cur.right;
}
//最后退出循环之后,单独打印整棵树的右边界
printRightEdge(head);
}

Morris遍历的应用

如何判断一棵树是否是搜索二叉树?

中序遍历升序就是搜索二叉树。

     public static boolean isBST(TreeNode head){
if(head == null) return true;
TreeNode cur = head;
TreeNode mostRight = null;
int preValue = Integer.MIN_VALUE;//上一次得到的值
while(cur != null){
mostRight = cur.left;
if(mostRight != null){
while(mostRight.right != null && mostRight != cur){
mostRight = mostRight.right;
}
if(mostRight.right == null){
mostRight.right = cur;
cur = cur.left;
continue;
}else {
mostRight.right = null;
}
}
//中序遍历的操作时机在这里 所以在这里进行判断
if(cur.val <= preValue){//没有递增
return false;
}
preValue = cur.val;
cur = cur.right;
}
return true;
}

总结

树型DP问题的套路:定义一个类收集树的信息,定义一个递归函数,递归地收集左子树的信息和右子树的信息,再整合得到以当前节点为根的树的信息。

什么时候用树型DP什么时候用Morris遍历?

当必须得到左树的信息和右树的信息后,再在当前节点整合二者信息后做出判断则用树型DP是最优解。

当不需要整合左树和右树信息的时候,可以用树型DP,但是Morris是最优解。

面试中很值得聊的二叉树遍历方法——Morris遍历的更多相关文章

  1. 为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

    我在面试 Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分, ...

  2. 剑指offer-第六章面试中的各项能力(二叉树的深度)

    题目:1:输入一个二叉树,求二叉树的深度.从根节点开始最长的路径. 思路:我们可以考虑用递归,求最长的路径实际上就是求根节点的左右子树中较长的一个然后再加上1. 题目2:输入一颗二叉树的根节点,判断该 ...

  3. 将Eclipse项目导入到Android studio 中 很多点9图出现问题解决方法

    在build.gradle里添加以下两句: aaptOptions.cruncherEnabled = false aaptOptions.useNewCruncher = false

  4. 【数据结构与算法】二叉树的 Morris 遍历(前序、中序、后序)

    前置说明 不了解二叉树非递归遍历的可以看我之前的文章[数据结构与算法]二叉树模板及例题 Morris 遍历 概述 Morris 遍历是一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1 ...

  5. 算法进阶面试题03——构造数组的MaxTree、最大子矩阵的大小、2017京东环形烽火台问题、介绍Morris遍历并实现前序/中序/后序

    接着第二课的内容和带点第三课的内容. (回顾)准备一个栈,从大到小排列,具体参考上一课.... 构造数组的MaxTree [题目] 定义二叉树如下: public class Node{ public ...

  6. Morris 遍历实现二叉树的遍历

    Morris 遍历实现二叉树的遍历 作者:Grey 原文地址: 博客园:Morris 遍历实现二叉树的遍历 CSDN:Morris 遍历实现二叉树的遍历 说明 Morris 遍历可以实现二叉树的先,中 ...

  7. 经典算法 Morris遍历

    内容: 1.什么是morris遍历 2.morris遍历规则与过程 3.先序及中序 4.后序 5.morris遍历时间复杂度分析 1.什么是morris遍历 关于二叉树先序.中序.后序遍历的递归和非递 ...

  8. 基于bs4库的HTML标签遍历方法

    基于bs4库的HTML标签遍历方法 import requests r=requests.get('http://python123.io/ws/demo.html') demo=r.text HTM ...

  9. 面试大总结之二:Java搞定面试中的二叉树题目

    package BinaryTreeSummary; import java.util.ArrayList; import java.util.Iterator; import java.util.L ...

随机推荐

  1. 解决Chrome插件安装时出现的“程序包无效”问题

    问题原因,新版的插件头部文件的修改,导致旧版的插件包无法使用,解决方式:解压. 1.把下载后的.crx扩展名的离线Chrome插件的文件扩展名改成.zip或者.rar(如果看不到Chrome插件的扩展 ...

  2. 数学--数论--HDU 6128 Inverse of sum (公式推导论)

    Description 给nn个小于pp的非负整数a1,-,na1,-,n,问有多少对(i,j)(1≤i<j≤n)(i,j)(1≤i<j≤n)模pp在意义下满足1ai+aj≡1ai+1aj ...

  3. 数学--数论-- HDU -- 2854 Central Meridian Number (暴力打表)

    A Central Meridian (ACM) Number N is a positive integer satisfies that given two positive integers A ...

  4. 2019 ICPC 银川网络赛 H. Fight Against Monsters

    It is my great honour to introduce myself to you here. My name is Aloysius Benjy Cobweb Dartagnan Eg ...

  5. keras-深度学习处理文本数据

    深度学习用于自然语言处理是将模式识别应用于单词.句子和段落,这与计算机视觉是将模式识别应用于像素大致相同.深度学习模型不会接收原始文本作为输入,它只能处理数值张量,因此我们必须将文本向量化(vecto ...

  6. andorid jar/库源码解析之错误提示

    目录:andorid jar/库源码解析 错误: 错误1: Error: Static interface methods are only supported starting with Andro ...

  7. 微信小程序下拉刷新时有部分区域不随着下拉移动

    问题 区域元素使用(position: fixed),小程序页面下拉刷新时,这部分区域不会随页面下拉移动. 如何解决 删除设置的top属性

  8. 一个数number的n次幂 python的pow函数

    @ 目录 解法1:暴力法 解法2:根据奇偶幂分类(递归法,迭代法,位运算法) 实现 pow(x, n),即计算 x 的 n 次幂函数.其中n为整数. 链接: pow函数的实现--leetcode. 解 ...

  9. Excel心得

    智能表.数据透视表.分类汇总表是Excel最值得学的技能. 通过插入表,或者快捷键Ctrl+T/Ctrl+L,可以将区域转换为智能表,保证了字段的公式的统一,而且版式自动化处理,一深一浅.Ctrl+Q ...

  10. 基于ELK搭建MySQL日志平台的要点和常见错误

    第一部分 概括 ELK是集分布式数据存储.可视化查询和日志解析于一体的日志分析平台.ELK=elasticsearch+Logstash+kibana,三者各司其职,相互配合,共同完成日志的数据处理工 ...