二叉樹的遞歸遍歷(JS實現)

相關概念

「樹的遍歷」 指按照必定規則不重複地訪問樹中全部節點的過程。
「訪問」指針對節點的操做,如打印節點的值,更新節點的值等。函數

本文討論二叉樹的遍歷,對節點的訪問經過打印節點的值體現出來。
從二叉樹的根節點出發,遍歷可分爲三個環節:post

  • 訪問節點(打印節點的值)
  • 遍歷節點的左子樹
  • 遍歷節點的右子樹

不一樣環節執行的前後順序產生了不一樣的遍歷方式。this

「前序遍歷」指先訪問節點,再遍歷節點的左子樹,最後遍歷節點的右子樹,按照這種規則不重複地訪問樹中全部節點的過程。
「中序遍歷」指先遍歷節點的左子樹,再訪問節點,最後遍歷節點的右子樹,按照這種規則不重複地訪問樹中全部節點的過程。
「後序遍歷」指先遍歷節點的左子樹,再遍歷節點的右子樹,最後訪問節點,按照這種規則不重複地訪問樹中全部節點的過程。spa

前序遍歷

clipboard.png

上圖展示了前序遍歷的整個過程,其中樹的結構用代碼表示以下(存儲爲變量root)翻譯

function Node(value) {
  this.value = value
  this.left = null
  this.right = null
}
root {
  value: 'A',
  left: {
      value: 'B',
      left: {
          value: 'D',
          left: {
              value: 'H',
              left: null,
              right: null
          },
          right: {
              value: 'I',
              left: null,
              right: null
          }
      },
      right: {
          value: 'E',
          left: null,
          right: null
      }
  },
  right: {
      value: 'C',
      left: {
          value: 'F',
          left: null,
          right: null
      },
      right: {
          value: 'G',
          left: null,
          right: null
      }
  }
}

設計一個函數,用於遍歷二叉樹,傳入的參數是二叉樹的根節點,函數會先訪問節點(打印節點的值),再遍歷節點的左子樹,最後遍歷節點的右子樹
上述代碼翻譯成代碼片斷就是設計

/**
 * 函數的做用是遍歷二叉樹
 * 傳入的參數是二叉樹的根節點
 * @param {object} root 
 */
function preOrderTraverse(root){
  console.log(root.value) // 訪問節點(打印節點的值)
  ... // 遍歷節點的左子樹
  ... // 遍歷節點的右子樹
}

... 處應該是遍歷節點的左,右子二叉樹的代碼。遍歷二叉樹不正是這個函數的做用嗎?故想到了遞歸指針

function preOrderTraverse(root){
  console.log(root.value) // 訪問節點(打印節點的值)
  preOrderTraverse(root.left) // 遍歷節點的左子樹
  preOrderTraverse(root.right) // 遍歷節點的右子樹
}

添加遞歸的終止條件,即訪問到葉節點就中止調用函數code

const preOrderTraverse = root => {
  console.log(root.value) // 訪問節點(打印節點的值)
  root.left && preOrderTraverse(root.left) // 若節點的左子樹存在,則遍歷節點的左子樹
  root.right && preOrderTraverse(root.right) // 若節點的右子樹存在,則遍歷節點的右子樹
}
preOrderTraverse(root)
// A B D H I E C F G

觸類旁通

中序遍歷blog

const inOrderTraverse = root => {
  root.left && inOrderTraverse(root.left) // 若節點的左子樹存在,則遍歷節點的左子樹
  console.log(root.value) // 訪問節點(打印節點的值)
  root.right && inOrderTraverse(root.right) // 若節點的右子樹存在,則遍歷節點的右子樹
}
inOrderTraverse(root)
// H D I B E A F C G

後序遍歷遞歸

const postOrderTraverse = root => {
  root.left && postOrderTraverse(root.left) // 若節點的左子樹存在,則遍歷節點的左子樹
  root.right && postOrderTraverse(root.right) // 若節點的右子樹存在,則遍歷節點的右子樹
  console.log(root.value) // 訪問節點(打印節點的值)
}
postOrderTraverse(root)
// H I D E B F G C A

非遞歸遍歷

隨着被調用次數的增長,遞歸函數會線性地增長棧空間的使用。至於二叉樹的非遞歸遍歷,且聽下回分解。

相關文章
相關標籤/搜索