关于二叉树的遍历,使用栈递归或者仿栈循环都是须要O(N)
的空间,Morris Traversal
保证了空间为O(1)
,时间仍是O(N)
(比原来多了一遍)。code
这里只介绍inOrder
顺序。递归
思路:io
对每个cur
节点,优先找到一个pre
节点,这个pre
节点的做用是,当后续cur
节点遍历 到这个位置时,能够直接经过这个pre
节点返回它须要返回的位置。console
例如:function
6 / \ 4 8 / \ 2 5
cur
节点在6
的时候,pre
节点会在5
,由于后面当cur
节点遍历到5
的时候,能够经过pre
节点直接返回6
cur
节点再4
的时候,pre
节点会在2
,当后面cur
到2
的时候,能够直接返回4
pre
找到了,是经过什么返回呢,由于不能修改二叉树结构,也不能使用堆栈记录。class
经过mirror
(镜像),也就是说,当找到pre
的时候(每一个pre
的右节点确保为null),在它的右节点建立一个镜像节点,
这个镜像节点直接指向当前的cur
节点。二叉树
这个操做是不占用空间的,由于只是互相引用。循环
例如:当上面的cur
为6
,pre
为5
,那么设置pre.right=cur
,感受上是这样:rsa
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 } } } }