Morris Traversal
昨天临近要睡觉的时候做了一个leetcode题目,“Recover BST”,算法很容易就想到了,直接找出两个异常点就好了,但是我写的算法是用栈实现的非递归遍历,空间复杂度是O(N),虽然比递归的要好点,但是题目说明里面那句“如果能用constant space ”却让我百思不得其解。难道我的解法不是最精简的?
我的解法说白了就是个preorder traversal,难道有可以不用traversal的办法,结果在discuss里面发现了一块代码,但是没怎么看明白了,接下来搜索资料,才知道这种解法叫“Morris Traversal”,实际上就是一种O(1)空间复杂度,O(N)空间复杂度的遍历方法。
我们知道,在深度搜索遍历的过程中,之所以要用递归或者是用非递归的栈方式,都是因为其他的方式没法记录当前节点的parent,而如果在每个节点的结构里面加个parent 分量显然是不现实的,那么Morris是怎么解决这一问题的呢?好吧,他用得很巧妙,实际上是用叶子节点的空指针来记录当前节点的位置,然后一旦遍历到了叶子节点,发现叶子节点的右指针指向的是当前节点,那么就认为以当前节点的左子树已经遍历完成。
以inorder为例,初始化当前节点为root,它的遍历规则如下:
- 如果当前节点为空,程序退出。
- 如果当前节点非空,
- 如果当前节点的左儿子为空,那么输出当前节点,当前节点重置为当前节点的右儿子。
- 如果当前节点的左儿子非空,找到当前节点左子树的最右叶子节点(此时最右节点的右儿子有两种情况,一种是指向当前节点,一种是为空,你也许感到奇怪,右节点的右儿子怎么可能非空,注意,这里的最右叶子节点只带的是原树中的最右叶子节点。),若其最右叶子节点为空,令其指向当前节点,将当前节点重置为其左儿子,若其最右节点指向当前节点,输出当前节点,将当前节点重置为当前节点的右儿子,并恢复树结构,即将最右节点的右节点再次设置为NULL
代码如下:
void MorrisTraversal(TreeNode * root){TreeNode *cur = root;TreeNode *rightmost;while(cur){if(cur->left){rightmost = cur->left;while(!rightmost->right&&rightmost->right!=cur){rightmost=rightmost->right;}if(!rightmost->right)rightmost->right = cur;else {cout<<cur->val<<" ";cur = cur->right;rightmost->right=NULL;}}else {cout<<cur->val<<" ";cur = cur->right;}}}
需要注意的是,世界上没有白吃的午餐,虽然这个遍历的时间复杂度还是O(N),但是此O(N)比之于用了栈的O(N)前面的系数还是要大些的,多出的耗费主要集中在寻找最右叶子。
对于preorder的情况,稍微将inorder的情况改写一下就好了。详情参考这篇文章。有人画了一幅图,其实这幅图看了作用也不大,只需要知道的是,本解法的关键在于,用当前节点的左子树的最右也自己点的右指针指向当前节点,使得恢复路径成为了可能。

Morris Traversal的更多相关文章
- Morris Traversal 二叉树遍历。
那天做了个SWAP NODE的题,要求constant space,不得不Morris Traversal. 稍微研究了一下,真正意义上的O(1)space对二叉树进行遍历.好像是1979年的算法. ...
- [转载]Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
本文主要解决一个问题,如何实现二叉树的前中后序遍历,有两个要求: 1. O(1)空间复杂度,即只能使用常数空间: 2. 二叉树的形状不能被破坏(中间过程允许改变其形状). 通常,实现二叉树的前序(pr ...
- Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)——无非是在传统遍历过程中修改叶子结点加入后继结点信息(传统是stack记录),然后再删除恢复
先看看线索二叉树 n个结点的二叉链表中含有n+1(2n-(n-1)=n+1)个空指针域.利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索 ...
- C前序遍历二叉树Morris Traversal算法
首先来递归算法,简单易懂: #include <stdio.h> #include <stdlib.h> #include <stdbool.h> typedef ...
- 二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)
例题 中序遍历94. Binary Tree Inorder Traversal 先序遍历144. Binary Tree Preorder Traversal 后序遍历145. Binary Tre ...
- 【LeetCode】 99. Recover Binary Search Tree [Hard] [Morris Traversal] [Tree]
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
- 额外空间复杂度O(1) 的二叉树遍历 → Morris Traversal,你造吗?
开心一刻 一天,有个粉丝遇到感情方面的问题,找我出出主意 粉丝:我女朋友吧,就是先天有点病,听不到人说话,也说不了话,现在我家里人又给我介绍了一个,我该怎么办 我:这个问题很难去解释,我觉得一个人活着 ...
- Morris Traversal方法遍历
实现二叉树的遍历且只需要O(1)的空间. 参考:http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html
- Data Structure Binary Tree: Morris traversal for Preorder
http://www.geeksforgeeks.org/morris-traversal-for-preorder/ #include <iostream> #include <v ...
随机推荐
- Recommender Systems协同过滤
第一部分是学习ID3时候积累的. 一.以前写的基础知识 1.信息:是用来消除不确定性的度量,信息量的大小,由所消除的不确定性的大小来计量(香农). 2.由于不确定性是由随机性引起的,所以用概率来描述和 ...
- hdu 4195
我本来的想法求这个三角形的外心~~ 可以得到三条边对应圆心角 则这个正多边形的一条边对应的圆心角 应可以整除这三个角 但是一开始 没想到暴力枚举边数n, 还用模板求圆心坐标 然后求圆心角 ...
- Spark的TorrentBroadcast:实现
依据Spark 1.4版 序列化和反序列化 前边提到,TorrentBroadcast的关键就在于特殊的序列化和反序列化设置.1.1版的TorrentBroadcast实现了自己的readObject ...
- Codeforces Round #242 (Div. 2) A~C
题目链接 A. Squats time limit per test:1 secondmemory limit per test:256 megabytesinput:standard inputou ...
- NYOJ 题目15 括号匹配(二)(区间DP)
点我看题目 题意 : 中文题不详述. 思路 : 本来以为只是个小模拟,没想到是个区间DP,还是对DP不了解. DP[i][j]代表着从字符串 i 位置到 j 位置需要的最小括号匹配. 所以初始化的DP ...
- Mac OS X 启用 Web 服务器
转载: http://note.rpsh.net/posts/2013/11/26/osx-apache-server-php-mysql/
- 算法Sedgewick第四版-第1章基础-001递归
一. 方法可以调用自己(如果你对递归概念感到奇怪,请完成练习 1.1.16 到练习 1.1.22).例如,下面给出了 BinarySearch 的 rank() 方法的另一种实现.我们会经常使用递归, ...
- php Late Static Bindings延迟静态绑定
官网说道: As of PHP 5.3.0, PHP implements a feature called late static bindings which can be used to ref ...
- JVM内存管理(一)
方法区: 方法区存放了要加载的类的信息(名称.修饰符等).类的静态变量.类中定义为final类型的常量.类中的field信息.类中的方法信息.当开发人员在程序中通过Class对象的getName.is ...
- devfs,proc,udev
devfs:常用的驱动函数封装 proc:在用户态检查内核状态的机制 udev 和 devfs相比? 一个是用户空间里的,一个运行在内核空间且被2.6以后版本抛弃了