二叉搜索樹的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
      }
    }
  }
}
相關文章
相關標籤/搜索