Morris遍历

一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1)

通过利用原树中大量空闲指针的方式,达到节省空间的目的

Morris遍历可以改前中后序的树遍历

思路:

创建一个当前节点cur 等于 head节点,再创建一个mostRight节点 表示最右节点

mostRight 当为空的时候表示是第一次来,如果mostRight 指向的是 cur 节点的位置,则代表已经来过一次了,现在是第二次,第二次来的时候直接mostRight置空就可以了

总流程是,当 cur 不为空的时候,mostRight 来到左孩子的位置,如果右孩子存在且是第一次来到这个节点,mostRight 遍历到最后

然后再判断,当前节点的右孩子是否为空,如果是空就把 mostRight 的右孩子指向 cur ,表示自己已经来过了一次,然后 cur 来到他左孩子的位置,继续循环的找。

如果当 cur 来到了 最右结点,发现右孩子是被修改过的,也就是之前修改成cur的指针,然后 cur 就会回到上次的 cur 的位置,回去之后,再把刚刚的那个结点右孩子改为空

就像一个线索一样,不断的执行这些过程,直到完成整个遍历

class Node
{
public:
Node(int data) :value(data){}
int value;
Node *left;
Node *right;
}; void morris(Node *head)
{
if (head == NULL)
{
return;
}
Node *cur = head;
Node *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;
cout << cur->value << " ";
cur = cur->left;
continue;
}
else {
mostRight->right = NULL;
}
}
cout << cur->value << " ";
cur = cur->right;
}
}

Morris改先序遍历

思路:

先序遍历的打印结果只需要把第一次遍历到的位置都输出,也就是除了第二次来到的位置都打印,就是先序遍历

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->right != NULL && mostRight->right != cur)
{
mostRight = mostRight->right;
}
if (mostRight->right == NULL)
{
mostRight->right = cur;
//第一次来了,直接返回上去
cout << cur->value << " ";
cur = cur->left;
continue;
}
else {
//第二次来到的
mostRight->right = NULL;
}
}
else {
//第一次来到的
cout << cur->value << " ";
}
cur = cur->right;
}
}

Morris改中序遍历

思路:

中序遍历,只需要把 能第二次来到的位置,第二次打印出来,第一次不打印,然后把只能来一次的位置第一次打印出来,就是中序遍历

void morrisMid(Node *head)
{
if (head == NULL)
{
return;
}
Node *cur = head;
Node *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;
}
}
cout << cur->value << " ";
cur = cur->right;
}
}

Morris后序遍历

思路:

把能进行二次遍历的结点 的右子树 逆序打印,最后再将 整个的右子树 逆序打印

Node* reverseEdge(Node *from)
{
Node *pre = NULL;
Node *next = NULL;
while (from != NULL)
{
next = from->right;
from->right = pre;
pre = from;
from = next;
}
return pre;
} void printEdge(Node *head)
{
Node *tail = reverseEdge(head);
Node *cur = tail;
while (cur != NULL)
{
cout << cur->value << " ";
cur = cur->right;
}
reverseEdge(tail);
} void morrisPos(Node *head)
{
if (head == NULL)
{
return;
}
Node *cur = head;
Node *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(head);
}

Morris遍历的更多相关文章

  1. 二叉树的遍历(递归,迭代,Morris遍历)

    二叉树的三种遍历方法: 先序,中序,后序,这三种遍历方式每一个都可以用递归,迭代,Morris三种形式实现,其中Morris效率最高,空间复杂度为O(1). 主要参考博客: 二叉树的遍历(递归,迭代, ...

  2. 二叉树的遍历(递归,迭代,Morris遍历)

    二叉树的遍历: 先序,中序,后序: 二叉树的遍历有三种常见的方法, 最简单的实现就是递归调用, 另外就是飞递归的迭代调用, 最后还有O(1)空间的morris遍历: 二叉树的结构定义: struct ...

  3. 【转载】Morris遍历二叉树 & BST(二叉搜索树) Traverse & 空间O(1) 时间O(n)

    因为做一道Leetcode的题目(前面博客有:link),需要用Space O(1)空间复杂度来中序遍历树, 看了Discuss,也上网搜了一下,发现空间O(1)可以用 Morris遍历的方法.方法介 ...

  4. 二叉树的Morris遍历

    二叉树的遍历,除了上篇文章中的传统递归和使用的栈结构的非递归方式,还有如下这种Morris遍历方式,该算法的构思非常巧妙:利用前驱空闲的rightChild指针指向当前节点,形成一个环.时间复杂度和前 ...

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

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

  6. 经典算法 Morris遍历

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

  7. 面试中很值得聊的二叉树遍历方法——Morris遍历

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

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

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

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

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

随机推荐

  1. rake

    ruby-rake https://rubygems.org/gems/rake 官方文档 https://ruby.github.io/rake/ http://docs.seattlerb.org ...

  2. Python程序结构

    Python程序结构 分支结构 条件语句: if 判断: .....满足判断条件执行的代码块 else: .....不满足判断条件执行的代码块 if 判断1: .....满足判断条件1执行的代码块 e ...

  3. IIS上传文件最大限制问题

    IIS服务器文件最大限制默认是30M. 自定义方法:修改配置文件,路径是:C:\Windows\System32\inetsrv\Config\applicationHost.config 在requ ...

  4. 不用写代码也能做表单 —— 加载meta即可

    做增删改查要写多少代码? 一个表单一套代码,十个表单十套代码吗? 我这么懒,怎么会写这么多代码? 我想做到:即使一百个表单也只需要一套代码(而且不需要复制粘贴).实现多个表单,只需要加载不同的meta ...

  5. 15个随机图片API

    15个随机图片API 妈妈再也不用担心我网站没图用了呜 请不要重复刷新此页面 ! 找了很久的说,你难道不想收藏一下吗 其中有些 API 速度并不太好,可能会拖慢贵站的速度 我也不能保证这些 API 能 ...

  6. Codeforces Round #670 (Div. 2) 深夜掉分(A - C题补题)

    1406A. Subset Mex https://codeforces.com/contest/1406/problem/A Example input 4 6 0 2 1 5 0 1 3 0 1 ...

  7. spring5源码 -- IOC容器设计理念和核心注解的作用

    一. spring源码整体脉络介绍及源码编译 二. Spring IOC的源码深入学习 2.1 ioc容器的加载过程(重要) 2.2 bean的生命周期源码深度剖析 2.3 循环依赖总结讲解 2.4 ...

  8. Linux设备驱动模型简述(源码剖析)

    1. Linux设备驱动模型和sysfs文件系统 Linux内核在2.6版本中引入设备驱动模型,简化了驱动程序的编写.Linux设备驱动模型包含设备(device).总线(bus).类(class)和 ...

  9. 内置函数:循环调用函数map和filter

    1.map:循环调用函数,前面一定一定要加list,要不然不会被调用 map的格式:list(map(函数名,循环体)) #这里的函数只能写函数名,不要加() list(map(os.mkdir,[' ...

  10. SDL开发笔记(二):音频基础介绍、使用SDL播放音频

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...