javaScript實現簡單的二叉樹


js 簡單版本的二叉樹實現 https://github.com/DaiHangLin/js-binary-tree

概念

  • 首先二叉樹是一顆樹,也就是每一個節點(除了root節點外)都存在其父節點,可能存在子節點,對於沒有子節點的節點稱之為葉子節點。

  • 而二叉樹是樹的一種特殊情形,也就是每個節點最多只有2個子節點。

  • 通常代表一棵樹的都是其跟節點

  • 樹的節點: 包含一個數據,以及最多兩個子節點,一般稱之為左節點,右節點

  • 節點層:跟節點的層定義為1,第一個子節點層定義為2,以此類推

  • 深度:最大的節點層代表了樹的深度

  • 滿二叉樹:除了最深一層(第三層)的葉子節點外,每個節點都有左右兩個葉子節點

              root (1)
             /    \
            l      r (2)
            /\     /\
           l  r   l  r (3)
    
  • 完全二叉樹:第二層必須是滿二叉樹,在滿二叉樹的基礎上,新增加了子節點,但是不全

          root (1)
         /    \
        l      r (2)
        /\     
       l  r        (3)
    

如何用數據結構表示一個樹

  • 樹由節點遞歸組成,所以首先需要定一個節點
    node = {
      value,
      left,
      right,
    }
    
    這個就可以簡單的理解為一個樹的節點,其中value代表節點的數據,left代表左子節點,right代表右子節點。
  • 如何生成一顆滿二叉樹
    • 思路:
      • 維護一個當前樹節點的鏈表中,初始值為跟節點。因為需要先進后出的概念,而對於js來說,可以用[]數組來直接實現鏈表,利用數據提供的方法
        • shift 從數組頭部移出數據
        • unshift 從數據頭部插入數據
        • push 從數組尾部插入數據
        • pop 從數組尾部移出數據
      • 從數組尾部取出第一個數據,也就是跟節點,判斷跟節點存不存在,如果不存在就創建,
      • 繼續重復從數組中取出下一個節點,先判斷左節點是否存在,如果不存在,則創建其左節點,如果存在,則將左節點保存到數組頭部中,然后同樣的判斷其右節點是否存在,如果不存在,則創建其右節點,如果存在,則將右節點保存到數組頭部中。
      • 每次新添加一個節點,則重復上面的兩個步驟
  insertTreeNode = (root, v) => {
    let queue = [root]
    while (true) {
      var node = queue.pop()
      if (!node.value) {
        node.value = v
        break
      }
      if (!node.left) {
        node.left = {value: v}
        break 
      } else {
        queue.unshift(node.left)
      }

      if (!node.right) {
        node.right = {value: v}
        break 
      } else {
        queue.unshift(node.right)
      }
    }
    console.log('tree', root)
  }

如何對二叉樹進行遍歷

  • 二叉樹的遍歷存在多種方式,常見的右 先序遍歷中序遍歷, 后序遍歷

       1    
      / \      
     2   3  
    
    • 先序遍歷 [1,2,3]
    • 中序遍歷 [2, 1, 3]
    • 后序遍歷 [3, 1, 2]
  • 編碼實現的思路:一顆樹實際上是由很多的子節點的組成的,所以問題首先可以拆解為如何遍歷一個節點,一個二叉樹的節點可能存在 "無", "沒有任何子節點", “存在一個左節點”,“存在一個右節點”,“存在一個左節點和一個右節點”

     function traverse(node) {
       if (node) {
         console.log(node.value)
       }
       if (node.left) {
         console.log(node.left.value)
       }
    
       if (node.right) {
         console.log(node.right.value)
       }
     }
    
  • 對於一顆樹而言,實際上只是對一個節點的擴展,也就是在遍歷時,遇到其子節點時,需要對子節點進行同樣的操作

  function traverseTree(root) {
    function doTraverse(node) {
      if (node) {
        console.log(node.value)
      }
      node.left && doTraverse(node.left)
      node.right && doTraverse(node.right)
    }
    doTraverse(root)
  }

如何根據前序遍歷,后序遍歷的結果重新構建一顆樹

  • 先來看一顆樹前序遍歷和中序遍歷結果的區別

        1    
       / \      
      2   3 
     / \
    4   5
    
    • 前序遍歷 preOrder: [1, 2, 4, 5, 3]
    • 中序遍歷 inOrder: [4, 2, 5, 1, 3]
  • 分析;

    • 分析前序遍歷: 前序遍歷的第一個數據,即為這顆樹的跟節點,但僅僅根據根節點和前序遍歷的數據,是不足夠重構一顆樹的,因為你不能保證這個樹其中的某個節點是一個完全節點(也就是左節點和右節點同時存在)。
    • 分析中序遍歷: 根據前序遍歷得到的結果,能直接推斷出 [4, 2, 5] 為 1 的 左節點並且是中序遍歷的結果, [3] 為 1 的右節點並且是中序遍歷的結果
    • 再次根據上一步推斷的結果,我們能推斷出,1左節點有3個元素,按照先序遍歷的結論,在 preOrder中,從頭部推出一個元素(unshift)后,接下來的3個節點,即為 1 的 左節點 並且是 先序遍歷的結果
    • 右節點的推論和左節點的推論一致,
  • 實現: 這里的實現,完全是按照上面的思路完成,沒有任何的優化。

revert = (p, i) => {
  if (!p || !i) {
    return
  }
  function doRevert(preorder, inorder, node = {}) {
    if (!preorder || !inorder || preorder.length == 0 || inorder.length == 0) {
      return
    }
    const first = preorder[0]
    if (!first) {
      return
    }
    const nextLeftPre = []
    const nextleftIn = []

    let index;
    for (index = 0; index < inorder.length; index ++) {
      if (first == inorder[index]) {
        break
      }
      nextleftIn.push(inorder[index])
    }
    const nextRightIn = inorder.slice(index + 1)
    index = 1;
    for (index; index < nextleftIn.length + 1; index  ++) {
      nextLeftPre.push(preorder[index])
    }

    const nextRightPre = preorder.slice(index)

    node = {
      value: first,
      left: nextLeftPre.length != 0 ? doRevert(nextLeftPre, nextleftIn, {}) : null,
      right: nextRightPre.length != 0 ? doRevert(nextRightPre, nextRightIn, {}) : null,
    }
    return node
  }
  let tree = {}
  tree = doRevert(p, i, tree)
  console.log('tree', tree)
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM