关于二叉树的遍历,使用栈递归或者仿栈循环都是需要O(N)
的空间,Morris Traversal
保证了空间为O(1)
,时间还是O(N)
(比原来多了一遍)。
这里只介绍inOrder
顺序。
思路:
对每一个cur
节点,优先找到一个pre
节点,这个pre
节点的作用是,当后续cur
节点遍历 到这个位置时,可以直接通过这个pre
节点返回它需要返回的位置。
例如:
6
/ \
4 8
/ \
2 5
- 上面当
cur
节点在6
的时候,pre
节点会在5
,因为后面当cur
节点遍历到5
的时候,可以通过pre
节点直接返回6
- 当
cur
节点再4
的时候,pre
节点会在2
,当后面cur
到2
的时候,可以直接返回4
pre
找到了,是通过什么返回呢,因为不能修改二叉树结构,也不能使用堆栈记录。
通过mirror
(镜像),也就是说,当找到pre
的时候(每个pre
的右节点确保为null),在它的右节点创建一个镜像节点,
这个镜像节点直接指向当前的cur
节点。
这个操作是不占用空间的,因为只是互相引用。
例如:当上面的cur
为6
,pre
为5
,那么设置pre.right=cur
,感觉上是这样:
6
/ \
4 8
/ \
2 5
\
6
/ \
4 8
...
其实并没有多出来那一块,只是5
引用到6
罢了
6
/ ↑ \
4 ↑ 8
/ \↑
2 5
理解了这些,那么后续就简单了,当cur
遍历到pre
的时候并且打印后,将pre
新增的引用删除恢复原来的树便可。
代码:
function morrisTraversal(root){
let cur=root,pre
while(cur!=null){
// 当左为空,直接打印
if(cur.left==null){
console.log(cur.val)
cur=cur.right
}else{
// 当左不为空,先去找 pre
pre=cur.left
while(pre.right!=null && pre.right!==cur){
pre=pre.right
}
// 建立引用,用于返回
if(pre.right==null){
pre.right=cur
cur=cur.left
}else{
// 删除引用
console.log(cur.val)
pre.right=null
cur=cur.right
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。