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. STL 训练 POJ - 1862 Stripies

    Description Our chemical biologists have invented a new very useful form of life called stripies (in ...

  2. ACM思维题训练 Section A

    题目地址: 选题为入门的Codeforce div2/div1的C题和D题. 题解: A:CF思维联系–CodeForces -214C (拓扑排序+思维+贪心) B:CF–思维练习-- CodeFo ...

  3. Codeforce 270D Greenhouse Effect

    Emuskald is an avid horticulturist and owns the world's longest greenhouse - it is effectively infin ...

  4. CF1316E Team Building

    CF1316E [Team Building] 状压dp,感觉比D简单 \(f[i][s]\),表示考虑前\(i\)个人,状态为\(s\)(\(s\)的第\(j-1\)个二进制位表示队员的第\(j\) ...

  5. SpringBoot返回JSON日期格式问题

    SpringBoot中默认返回的日期格式类似于这样: "birth": 1537407384500 或者是这样: "createTime": "201 ...

  6. F. Cards and Joy

    F. Cards and Joy 题目大意: 给你n个人,每一个人恰好选k张牌. 第一行是 n 和 k 第二行有n*k个数,代表有n*k张牌,每张牌上的数字 第三行有n个数,代表第i个人喜欢的数字 第 ...

  7. 系统通配符号、系统正则符号,grep

    系统通配符号.系统正则符号,grep 1 系统通配符号 系统通配符号:借助通配符号 匹配文件名称信息 1.1 *: 匹配所有(任意)字符信息 找寻以old开头的文件 find /oldboy -typ ...

  8. spring学习笔记(九)事务学习(上)

    前述 ​ 这段时间在工作中碰到一个事务相关的问题.先说下这个问题的场景,我们是一个商城项目,正在开发优惠券模块,现在有一个需求是需要批量领取优惠券,而且在领券时,其中一张领取失败不能影响其他符合要求的 ...

  9. 201771030115-牛莉梅 实验一 软件工程准备-<初学《构建之法--现代软件工程》的疑问>

    项目 内容 课程班级博客链接 https://edu.cnblogs.com/campus/xbsf/nwnu2020SE 这个作业要求链接 https://www.cnblogs.com/nwnu- ...

  10. puamap是什么意思

    artists map 定义格式:[puamap代号 名] 相关属性: 1.FIGHT 2.SAFE 安全区域 3.DARK 4.NEEDHOLE 配合mapinfo里 x,y -> x1,y1 ...