數據結構與算法對的javaScript描述-二叉搜索樹

定義

樹以及相關概念

二叉搜索樹的定義

  • 首先是二叉樹java

    • 最多有兩個節點,分別是左右節點
  • 子節點的位置是肯定的,變現爲node

    • 左子節點的值小於其父節點
    • 右子節點的值大於其父節點

BST在JS中的描述

JS描述的完整代碼傳送門
可視化BST傳送門

節點類 Node

樹是由節點組成的,要實現樹那麼先要實現節點

節點的要素

  • data:每一個節點都須要有一個數值
  • left:左子節點,在沒有左子節點的時候指向空對象null
  • right: 右子節點,在沒有右子節點的時候指向空對象null
  • count: 計數值,累計插入的次數,這個是非必須的,做爲一個要素是爲了後續處理插入重複值的問題
  • show: 顯示方法,展現該節點的值(和插入次數),在這裏返回一個包含值(和插入次數)的對象

js的描述

class Node {
  constructor (data,
               count = 1,
               left = null,
               right = null)
  {
    // 數值
    this.data = data
    // 出現次數
    this.count = count
    // 左右節點指向
    this.left = left
    this.right = right
  }
  show () {
    return {
      data: this.data,
      count: this.count
    }
  }
}

BST 二叉搜索樹類

屬性

目前咱們只須要一個根節點的屬性,所以一個基本的BST能夠描述爲:git

class BST {
  constructor () {
    // 初始化跟節點爲null
    this.root = null
  }
}

插入方法

咱們有了一個基本的BST,這時候咱們能夠new一個 bst,那麼咱們怎麼插入數據呢?這時候咱們須要一個insert方法,這個方法有如下的條件:github

  • 插入的是節點,也就是上述的類Node的一個實例
  • 若是沒有根節點,bst的根節點指向該節點
  • 若是有根節點則向下遍歷,找到合適的位置插入該節點,遍歷規則以下圖:

帶有插入方法的BSTjs的描述以下

class BST {
  constructor () {
    // 初始化跟節點爲null
    this.root = null
  }
  /**
   * 插入數據
   * @param data
   */
  insert (data) {
    let n = new Node(data, 1)
    if (this.root === null) {
      // 沒有根節點,新的樹把待插入的值做爲根節點
      this.root = n
    } else {
      // 有根節點,遍歷樹直到找到合適的位置
      let current = this.root
      while (true) {
        if (data < current.data) {
          if (current.left === null) {
            current.left = n
            break
          }
          current = current.left
        } else if (data === current.data) {
          current.count += 1
          break
        } else {
          if (current.right === null) {
            current.right = n
            break
          }
          current = current.right
        }
      }
    }
  }
}

插入一組測試數據測試

let testData = [
  43,
  34,
  67,
  23,
  34,
  45,
  2,
  78,
  34
]

let bst = new BST()
console.log(JSON.stringify(bst))

for (let data of testData) {
  bst.insert(data)
}
console.log(JSON.stringify(bst))

插入數據前:web

{"root":null}

插入數據後bash

{
    "root": {
        "data": 43,
        "count": 1,
        "left": {
            "data": 34,
            "count": 3,
            "left": {
                "data": 23,
                "count": 1,
                "left": {
                    "data": 2,
                    "count": 1,
                    "left": null,
                    "right": null
                },
                "right": {
                    "data": 28,
                    "count": 1,
                    "left": null,
                    "right": null
                }
            },
            "right": null
        },
        "right": {
            "data": 67,
            "count": 1,
            "left": {
                "data": 45,
                "count": 1,
                "left": null,
                "right": null
            },
            "right": {
                "data": 78,
                "count": 1,
                "left": null,
                "right": null
            }
        }
    }
}

插入數據以後咱們是經過nodejs的logger來查看bst,事實上,咱們還須要其餘的遍歷方法來查看bstpost

BST的遍歷

遍歷二叉樹一般有三種遍歷方法,分別是中序、先序和後序,他們的遍歷路徑不同

中序遍歷

中序遍歷應該是最經常使用的一種遍歷方法

js中的描述測試

/**
   * 中序遍歷
   * @param node
   */
  inOrder (node) {
    if (node !== null) {
      this.inOrder(node.left)
      console.log(`data:${node.data},count:${node.count}`)
      this.inOrder(node.right)
    }
  }

上述例子中的輸出結果this

中序
data:2,count:1
data:23,count:1
data:28,count:1
data:34,count:3
data:43,count:1
data:45,count:1
data:67,count:1
data:78,count:1

路徑圖
spa

先序遍歷

js中的描述

/**
   * 先序遍歷
   * @param node
   */
  preOrder (node) {
    if (node !== null) {
      console.log(`data:${node.data},count:${node.count}`)
      this.preOrder(node.left)
      this.preOrder(node.right)
    }
  }

上述例子中的輸出結果

先序
data:43,count:1
data:34,count:3
data:23,count:1
data:2,count:1
data:28,count:1
data:67,count:1
data:45,count:1
data:78,count:1

路徑圖

後序遍歷

js中的描述

/**
   * 後序遍歷
   * @param node
   */
  postOrder (node) {
    if (node !== null) {
      this.postOrder(node.left)
      this.postOrder(node.right)
      console.log(`data:${node.data},count:${node.count}`)
    }
  }

上述例子中的輸出結果

後序
data:2,count:1
data:28,count:1
data:23,count:1
data:34,count:3
data:45,count:1
data:78,count:1
data:67,count:1
data:43,count:1

路徑圖

查找

在二叉搜索樹中查找數據很是簡單

最大值與最小值

最小值爲樹中的最左邊的葉子節點獲得值,最大值爲最右邊子節點的值
/**
   * 查找最小值
   * @returns {CanvasPixelArray|string|Object[]|*}
   */
  getMin (node) {
    let current = node || this.root
    while (current.left !== null) {
      current = current.left
    }
    return current
  }
  
  /**
   * 查找最大值
   * @returns {CanvasPixelArray|string|Object[]|*}
   */
  getMax (node) {
    let current = node || this.root
    while (current.right !== null) {
      current = current.right
    }
    return current
  }

查找指定數據

根據數據的大小判斷向左向右查找使得查找很是有效率
/**
   * 查找數據
   * @param data
   * @returns {*}
   */
  find (data) {
    let current = this.root,
      result = null
    while (current !== null) {
      if (data === current.data) {
        result = current
        break
      } else if (data < current.data) {
        current = current.left
      } else {
        current = current.right
      }
    }
    return result
  }
bst.getMax().data // 78
bst.getMax().count // 1
bst.getMin().data // 2
bst.getMin().count // 1

刪除數據

刪除數據實際上是操做二叉搜索樹中最麻煩的一部分

個人思路以下:

  • 待刪除節點沒有子節點:

    • 父節點鏈向該節點的連接指向null
  • 待刪除節點只有左節點或者右節點

    • 父節點鏈向他的連接指向他的子節點
  • 待刪除節點既有左節點又有右節點

    • 用他的右子樹的最小節點取代(值和計數替代)待刪除節點
    • 在他的右子樹中刪除右子樹中的最小節點

基於此,JS中的描述爲

/**
   * 刪除數據
   * @param data
   */
  remove ( data ) {
    this.root = this.removeDataFromNode(this.root, data)
  }
  
  /**
   * 從指定節點中刪除數據
   * @param node
   * @param data
   * @returns {*}
   */
  removeDataFromNode (node, data) {
    if (node !== null) {
      if (data === node.data) {
        if (node.left === null && node.right === null) {
        //  沒有子節點
          return null
        }
        if (node.right === null) {
        //  只有左節點
          return node.left
        }
        if (node.left === null) {
        //  只有右節點
          return node.right
        }
        // 有作節點和右節點
        // 取右節點的最小值
        let tempNode = this.getMin(node.right)
        node.data = tempNode.data
        node.count = tempNode.count
        node.right = this.removeDataFromNode(node.right, tempNode.data)
        return node
      } else if (data < node.data) {
        node.left = this.removeDataFromNode(node.left, data)
        return node
      } else {
        node.right = this.removeDataFromNode(node.right, data)
        return node
      }
    } else {
      return null
    }
  }
相關文章
相關標籤/搜索