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. vue 组件内的守卫

    1.beforeRouteEnter ()  // 进入该组件之前要去进行的逻辑操作, 2.beforeRouteLeave() // 离开该组件之前要去进行的逻辑操作(可清除定时器等耗用内存的变量, ...

  2. Lua 5.3注册C++类相关API

    int luaL_newmetatable (lua_State *L, const char *tname); 如果注册表中不存在名为tname的表,则在注册表中创建一个名为tname的表,并将这个 ...

  3. 9.深入k8s:调度器及其源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 这次讲解的是k8s的调度器部分的代码,相对来说比较复杂,慢慢的梳理清 ...

  4. .net core中使用jwt进行认证

    JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息.由于此信息是经过数字签名的,因此可以被验证和信任 ...

  5. Johnson全源最短路

    例题:P5905 [模板]Johnson 全源最短路 首先考虑求全源最短路的几种方法: Floyd:时间复杂度\(O(n^3)\),可以处理负权边,但不能处理负环,而且速度很慢. Bellman-Fo ...

  6. 在遍历ResultSet的循环中再执行SQL会发生什么(前提:同一个Statement)

    如下面代码: Class.forName(DBParam.Driver).newInstance(); conn = DriverManager.getConnection(DBParam.DbUrl ...

  7. Bypass windous/mac 登陆密码

    前言 如题,在52破解里看到一个非常好用的工具 Kon-Boot 2.7 功能 不会去擦除windows密码 不会修改windows文件 此外,Kon-Boot的最新版是目前世界上唯一的一个能够绕过W ...

  8. Linux下vim的安装及配置

    目录 一.vim的下载 二.vim的基本知识 三.vim的基本配置 四.vim与外部文件的复制粘贴 一.vim的下载 Ubuntu系统,输入命令: sudo apt install vim Cento ...

  9. 移动APP性能评测与优化

    本文是<移动App性能评测与优化>的读书笔记. PS:说是读书笔记,其实就是摘录. 移动App的性能测试主要包括:内存使用情况.电量消耗.功能的流畅度等: 1. 内存 1.1 内存的主要组 ...

  10. 微信开发者工具集成GitHub,多人协调开发,上传拉取等

    一,准备环境 1,提前安装git环境和GitHub做集成,不做多解释: 1,准备微信项目代码: 2,创建GitHub仓库: 二,创建GitHub仓库 1,创建一个空的GitHub仓库,不要任何文件和不 ...