說說你對樹的理解?相關的操作有哪些?

林恒發表於2024-04-17

一、是什麼

在計算機領域,樹形資料結構是一類重要的非線性資料結構,可以表示資料之間一對多的關係。以樹與二叉樹最為常用,直觀看來,樹是以分支關係定義的層次結構

二叉樹滿足以下兩個條件:

  • 本身是有序樹
  • 樹中包含的各個結點的不能超過 2,即只能是 0、1 或者 2

如下圖,左側的為二叉樹,而右側的因為頭結點的子結點超過2,因此不屬於二叉樹:

同時,二叉樹可以繼續進行分類,分成了滿二叉樹和完成二叉樹:

  • 滿二叉樹:如果二叉樹中除了葉子結點,每個結點的度都為 2

  • 完成二叉樹:如果二叉樹中除去最後一層節點為滿二叉樹,且最後一層的結點依次從左到右分佈

二、操作

關於二叉樹的遍歷,常見的有:

  • 前序遍歷

  • 中序遍歷

  • 後序遍歷

  • 層序遍歷

前序遍歷

前序遍歷的實現思想是:

  • 訪問根節點
  • 訪問當前節點的左子樹
  • 若當前節點無左子樹,則訪問當前節點的右子

根據遍歷特性,遞迴版本用程式碼表示則如下:

const preOrder = (root) => {
  if(!root){ return }
  console.log(root)
  preOrder(root.left)
  preOrder(root.right)
}

如果不使用遞迴版本,可以藉助棧先進後出的特性實現,先將根節點壓入棧,再分別壓入右節點和左節點,直到棧中沒有元素,如下:

const preOrder = (root) => {
  if(!root){ return }
  const stack = [root]
  while (stack.length) {
    const n = stack.pop()
    console.log(n.val)
    if (n.right) {
      stack.push(n.right)
    }
    if (n.left) {
      stack.push(n.left)
    }
  }
}

中序遍歷

前序遍歷的實現思想是:

  • 訪問當前節點的左子樹
  • 訪問根節點
  • 訪問當前節點的右子

遞迴版本很好理解,用程式碼表示則如下:

const inOrder = (root) => {
  if (!root) { return }
  inOrder(root.left)
  console.log(root.val)
  inOrder(root.right)
}

非遞迴版本也是藉助棧先進後出的特性,可以一直首先一直壓入節點的左元素,當左節點沒有後,才開始進行出棧操作,壓入右節點,然後有依次壓入左節點,如下:

const inOrder = (root) => {
  if (!root) { return }
  const stack = [root]
  let p = root
  while(stack.length || p){
    while (p) {
      stack.push(p)
      p = p.left
    }
    const n = stack.pop()
    console.log(n.val)
    p = n.right
  }
}

後序遍歷

前序遍歷的實現思想是:

  • 訪問當前節點的左子樹
  • 訪問當前節點的右子
  • 訪問根節點

遞迴版本,用程式碼表示則如下:

const postOrder = (root) => {
  if (!root) { return }
  postOrder(root.left)
  postOrder(root.right)
  console.log(n.val)
 }

後序遍歷非遞迴版本實際根全序遍歷是逆序關係,可以再多建立一個棧用來進行輸出,如下:

const preOrder = (root) => {
  if(!root){ return }
  const stack = [root]
  const outPut = []
  while (stack.length) {
    const n = stack.pop()
    outPut.push(n.val)
    if (n.right) {
      stack.push(n.right)
    }
    if (n.left) {
      stack.push(n.left)
    }
  }
  while (outPut.length) {
    const n = outPut.pop()
    console.log(n.val)
  }
}

層序遍歷

按照二叉樹中的層次從左到右依次遍歷每層中的結點

藉助佇列先進先出的特性,從樹的根結點開始,依次將其左孩子和右孩子入隊。而後每次佇列中一個結點出隊,都將其左孩子和右孩子入隊,直到樹中所有結點都出隊,出隊結點的先後順序就是層次遍歷的最終結果

用程式碼表示則如下:

const levelOrder = (root) => {
    if (!root) { return [] }
    const queue = [[root, 0]]
    const res = []
    while (queue.length) {
        const n = queue.shift()
        const [node, leval] = n
        if (!res[leval]) {
            res[leval] = [node.val]
        } else {
            res[leval].push(node.val)
        }
        if (node.left) { queue.push([node.left, leval + 1]) }
        if (node.right) { queue.push([node.right, leval + 1]) }
    }
    return res
};

三、總結

樹是一個非常重要的非線性結構,其中二叉樹以二叉樹最常見,二叉樹的遍歷方式可以分成前序遍歷、中序遍歷、後序遍歷

同時,二叉樹又分成了完成二叉樹和滿二叉樹

參考文獻

  • https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%A0%91
  • http://data.biancheng.net/view/27.html

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。

說說你對樹的理解?相關的操作有哪些?

相關文章