【資料結構與演算法】二叉搜尋樹的簡單封裝

鉛筆心發表於2020-04-05

零、我想說

這是在對二叉搜尋樹的學習過程中的小記錄, 如果有錯誤的地方,歡迎大家和我一起討論學習,一起進步~

一、 二叉搜尋樹的結構

【資料結構與演算法】二叉搜尋樹的簡單封裝

【程式碼實現】

class Node{
      constructor(data, lChild, rChild) {
        this.data = data     //節點儲存的資料
        this.lChild = lChild //節點的左孩子
        this.rChild = rChild //節點的右孩子
      }
    }
    class BinarySearchTree {
      constructor () {
        this.root = null    //二叉搜尋樹的根節點
      }
    }
複製程式碼

二、二叉搜尋樹插入節點

【資料結構與演算法】二叉搜尋樹的簡單封裝

【思路】

  1. 新建待插入節點
  2. 定義插入方法(根據二叉搜尋樹的特點:對於任意一個結點,其左子樹中最大關鍵字都不超過x.data,其右子樹中最小關鍵字都不小於x.data)
  3. 如果根節點為空的話,將插入節點賦值給根節點,否則遞迴呼叫步驟2中的插入方法

【程式碼實現】

  insert(data) {
    //1.新建節點
    let newNode = new Node(data, null, null)
    //2.定義插入的方法(根據二叉搜尋樹的特點):
    //給方法插入兩個引數:開始查詢的節點,待插入節點
    /*
       2.1 如果待插入節點比當前節點小的話,
    *      如果當前節點的左孩子為空,則直接賦值給當前節點的左孩子,
           否則遞迴呼叫插入方法,也就是找到空的左節點
           注意開始查詢的節點應該是當前節點的左孩子
        
       2.1 如果待插入節點比當前節點大的話,
    *      如果當前節點的右孩子為空,則直接賦值給當前節點的右孩子,
           否則遞迴呼叫插入方法,也就是找到空的右節點
           注意開始查詢的節點應該是當前節點的右孩子
    */
    let insertNode = (currentNode, newNode) => {
      if (newNode.data < currentNode.data) {
        if (currentNode.lChild === null) {
          currentNode.lChild = newNode
        } else {
          insertNode(currentNode.lChild, newNode)
        }
      } else {
        if (currentNode.rChild === null) {
          currentNode.rChild = newNode
        } else {
          insertNode(currentNode.rChild, newNode)
        }
      }
    }
    //3.如果根節點為空,直接賦值給空節點
    if (this.root === null) {
      this.root = newNode
    } else {
      //否則呼叫插入節點的方法
      insertNode(this.root, newNode)
    }
  }
複製程式碼

【測試程式碼】

let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst)
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝 【資料結構與演算法】二叉搜尋樹的簡單封裝

三、二叉樹的遍歷

遍歷二叉樹,就是以某種方式逐個“訪問”二叉樹的每一個節點。
“訪問”是指對節點的進行某種操作,例如輸出節點的值。根據訪問樹根的順序,
我們有三種方式遍歷二叉樹:

  1. 前序遍歷:樹根->左子樹->右子樹

  2. 中序遍歷:左子樹->樹根->右子樹

  3. 後序遍歷:左子樹->右子樹->樹根

1.先序遍歷

【資料結構與演算法】二叉搜尋樹的簡單封裝

【思路】
  1. 用陣列儲存結果
  2. 定義一個方法:如果是空節點則return fase否則按順序樹根->左子樹->右子樹遞迴呼叫該方法
  3. 自動呼叫:從根節點開始訪問
  4. 返回結
【程式碼實現】
  preOrder() {
    let result = []    //用於儲存結果
    let preOrderNode = (node) => {
      if (node === null) return false
      result.push(node.data)   //訪問節點的data,將data放入result陣列中
      preOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
      preOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
    } 
    preOrderNode(this.root) // 自動呼叫:從根節點開始訪問
    return result
  }
複製程式碼

【程式碼測試】

let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.preOrder())
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝

2.中序遍歷

【資料結構與演算法】二叉搜尋樹的簡單封裝

【思路】
  1. 用陣列儲存結果
  2. 定義一個方法:如果是空節點則return fase否則按順序左子樹->樹根->右子樹遞迴呼叫該方法
  3. 自動呼叫:從根節點開始訪問
  4. 返回結果
【程式碼實現】
  midOrder() {
    let result = []    //用於儲存結果
    let midOrderNode = (node) => {
      if (node === null) return false
      midOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
      result.push(node.data)   //訪問節點的data,將data放入result陣列中
      midOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
    } 
    midOrderNode(this.root) // 自動呼叫:從根節點開始訪問
    return result
  }
複製程式碼

【程式碼測試】

let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.midOrder())
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝

3.後序遍歷

【資料結構與演算法】二叉搜尋樹的簡單封裝

【思路】
  1. 用陣列儲存結果
  2. 定義一個方法:如果是空節點則return fase否則按順序左子樹->右子樹->樹根遞迴呼叫該方法
  3. 自動呼叫:從根節點開始訪問
  4. 返回結果
【程式碼實現】
  lastOrder() {
    let result = []    //用於儲存結果
    let lastOrderNode = (node) => {
      if (node === null) return false
      lastOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
      lastOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
      result.push(node.data)   //訪問節點的data,將data放入result陣列中
    } 
    lastOrderNode(this.root) // 自動呼叫:從根節點開始訪問
    return result
  }
複製程式碼

【程式碼測試】

let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.lastOrder())
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝

四、根據data查詢節點

【思路】

  1. 定義一個查詢的方法:
    方法有兩個引數:一個是開始查詢的節點,另一個是查詢的data
    如果查詢的值比當前值小的話,就遞迴呼叫該方法(注意用查詢節點的左孩子開始查詢),否則從查詢節點的右孩子開始查詢
  2. 自動呼叫方法

【程式碼實現】

  searchNodeByData(data) {
    //定義一個查詢的方法
    //方法有兩個引數:一個是開始查詢的節點,另一個是查詢的data
    const search = (fromNode, data) => {
      console.log(fromNode)
      if (fromNode === null) return false  //遞迴結束條件
      if (fromNode.data === data) return fromNode  //如果找到,則返回節點
      //如果查詢的值比當前值小的話,就遞迴呼叫該方法(注意用查詢節點的左孩子開始查詢),否則從查詢節點的右孩子開始查詢
      return search((data  < fromNode.data ? fromNode.lChild: fromNode.rChild), data)
    }
    return search(this.root, data) //自動呼叫方法
  }
複製程式碼

【測試程式碼】

   let bst = new BinarySearchTree()
   bst.insert(5);
   bst.insert(3);
   bst.insert(6);
   bst.insert(2);
   bst.insert(4);
   bst.insert(8);
   console.log(bst.searchNodeByData(0))
   console.log(bst.searchNodeByData(5))
   console.log(bst.searchNodeByData(4))
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝

五、二叉樹查詢最值

1.查詢最大值

【思路】

根據二叉搜尋樹的特點,最右邊的孩子就是最大的節點

【程式碼實現】

  maxFrom(node) {
    //定義查詢最大節點的的方法
    const maxNode = node => {
      if (node === null) return false  //遞迴結束的條件
      if (node.rChild == null) return node //該節點沒有右孩子了
      return maxNode(node.rChild)  //否則一直遞迴查詢右孩子
    }
    return maxNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
  }
複製程式碼

【測試程式碼】

let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.maxFrom())
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝

1.查詢最小值

【思路】

根據二叉搜尋樹的特點,最左邊的孩子就是最小的節點

【程式碼實現】

  minFrom(node) {
    //定義查詢最小節點的的方法
    const minNode = node => {
      if (node === null) return false   //遞迴結束的條件
      if (node.lChild === null) return node //該節點沒有左孩子了
      return minNode(node.lChild)  //否則一直遞迴查詢右孩子
    }
    return minNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
  }
複製程式碼

【測試程式碼】

let bst = new BinarySearchTree()
bst.insert(5);
bst.insert(3);
bst.insert(6);
bst.insert(2);
bst.insert(4);
bst.insert(8);
console.log(bst.minFrom())
複製程式碼

【測試結果】

【資料結構與演算法】二叉搜尋樹的簡單封裝

六、我的程式碼

 class Node{
      constructor(data, lChild, rChild) {
        this.data = data     //節點儲存的資料
        this.lChild = lChild //節點的左孩子
        this.rChild = rChild //節點的右孩子
      }
    }
    class BinarySearchTree {
      constructor () {
        this.root = null    //二叉搜尋樹的根節點
      }
      insert(data) {
        //1.新建節點
        let newNode = new Node(data, null, null)
        //2.定義插入的方法(根據二叉搜尋樹的特點):
        //給方法插入兩個引數:開始查詢的節點,待插入節點
        /*
           2.1 如果待插入節點比當前節點小的話,
        *      如果當前節點的左孩子為空,則直接賦值給當前節點的左孩子,
               否則遞迴呼叫插入方法,也就是找到空的左節點
               注意開始查詢的節點應該是當前節點的左孩子
            
           2.1 如果待插入節點比當前節點大的話,
        *      如果當前節點的右孩子為空,則直接賦值給當前節點的右孩子,
               否則遞迴呼叫插入方法,也就是找到空的右節點
               注意開始查詢的節點應該是當前節點的右孩子
        */
        let insertNode = (currentNode, newNode) => {
          if (newNode.data < currentNode.data) {
            if (currentNode.lChild === null) {
              currentNode.lChild = newNode
            } else {
              insertNode(currentNode.lChild, newNode)
            }
          } else {
            if (currentNode.rChild === null) {
              currentNode.rChild = newNode
            } else {
              insertNode(currentNode.rChild, newNode)
            }
          }
        }
        //3.如果根節點為空,直接賦值給空節點
        if (this.root === null) {
          this.root = newNode
        } else {
          //否則呼叫插入節點的方法
          insertNode(this.root, newNode)
        }
      }
      //前序遍歷
      preOrder() {
        let result = []    //用於儲存結果
        let preOrderNode = (node) => {
          if (node === null) return false
          result.push(node.data)   //訪問節點的data,將data放入result陣列中
          preOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
          preOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
        } 
        preOrderNode(this.root) // 自動呼叫:從根節點開始訪問
        return result
      }
      //中序遍歷
      midOrder() {
        let result = []    //用於儲存結果
        let midOrderNode = (node) => {
          if (node === null) return false
          midOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
          result.push(node.data)   //訪問節點的data,將data放入result陣列中
          midOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
        } 
        midOrderNode(this.root) // 自動呼叫:從根節點開始訪問
        return result
      }
      //後續遍歷
      lastOrder() {
        let result = []    //用於儲存結果
        let lastOrderNode = (node) => {
          if (node === null) return false
          lastOrderNode(node.lChild)//節點的左孩子 遞迴呼叫該遍歷的方法
          lastOrderNode(node.rChild)//節點的右孩子 遞迴呼叫該遍歷的方法
          result.push(node.data)   //訪問節點的data,將data放入result陣列中
        } 
        lastOrderNode(this.root) // 自動呼叫:從根節點開始訪問
        return result
      }
      //根據data查詢節點
      searchNodeByData(data) {
        //定義一個查詢的方法
        //方法有兩個引數:一個是開始查詢的節點,另一個是查詢的data
        const search = (fromNode, data) => {
          if (fromNode === null) return false  //遞迴結束條件
          if (fromNode.data === data) return fromNode  //如果找到,則返回節點
          //如果查詢的值比當前值小的話,就遞迴呼叫該方法(注意用查詢節點的左孩子開始查詢),否則從查詢節點的右孩子開始查詢
          return search((data  < fromNode.data ? fromNode.lChild: fromNode.rChild), data)
        }
        return search(this.root, data) //自動呼叫方法
      }
      maxFrom(node) {
        //定義查詢最大節點的的方法
        const maxNode = node => {
          if (node === null) return false  //遞迴結束的條件
          if (node.rChild == null) return node //該節點沒有右孩子了
          return maxNode(node.rChild)  //否則一直遞迴查詢右孩子
        }
        return maxNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
      }
      minFrom(node) {
        //定義查詢最小節點的的方法
        const minNode = node => {
          if (node === null) return false   //遞迴結束的條件
          if (node.lChild === null) return node //該節點沒有左孩子了
          return minNode(node.lChild)  //否則一直遞迴查詢右孩子
        }
        return minNode(node || this.root) //如果沒有指定從哪個節點找起的話,預設從根節點開始查詢
      }
    }

複製程式碼

相關文章