二叉搜索树的Morris中序遍历(O(1)空间)思路

关于二叉树的遍历,使用栈递归或者仿栈循环都是须要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,当后面cur2的时候,能够直接返回4

pre找到了,是经过什么返回呢,由于不能修改二叉树结构,也不能使用堆栈记录。class

经过mirror(镜像),也就是说,当找到pre的时候(每一个pre的右节点确保为null),在它的右节点建立一个镜像节点,
这个镜像节点直接指向当前的cur节点。二叉树

这个操做是不占用空间的,由于只是互相引用。循环

例如:当上面的cur6pre5,那么设置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
      }
    }
  }
}
相关文章
相关标签/搜索