JavaScript >> Javascript 文檔 >  >> JavaScript

JavaScript 中的計算機科學:二叉搜索樹,第 1 部分

在計算機科學課程中,最常使用和討論的數據結構之一可能是二叉搜索樹。這通常是引入的第一個具有非線性插入算法的數據結構。二叉搜索樹類似於雙向鍊錶,每個節點都包含一些數據以及指向其他節點的兩個指針。它們在這些節點相互關聯的方式上有所不同。二叉搜索樹節點的指針通常稱為“左”和“右”,以指示與當前值相關的值的子樹。此類節點的簡單 JavaScript 實現如下:

var node = {
    value: 125,
    left: null,
    right: null
};

從名稱可以看出,二叉搜索樹被組織成層次樹結構。第一項成為根節點,每個附加值都作為該根的祖先添加到樹中。然而,二叉搜索樹的獨特之處在於節點根據它們包含的值進行排序:作為節點左子樹一部分的任何值總是小於節點的值,而右子樹中的任何值總是大於節點的值。這樣,在二叉搜索樹中查找值變得非常簡單,只要您要查找的值小於您正在處理的節點,就向左走,如果該值更大,則向右走。二叉搜索樹中不能有重複項,因為重複項會破壞這種關係。下圖表示一個簡單的二叉搜索樹。

二叉搜索樹圖

此圖表示一棵二叉搜索樹,其根值為 8。當添加值 3 時,它成為根的左孩子,因為 3 小於 8。當添加值 1 時,它成為 3 的左孩子,因為1 小於 8(所以向左走)然後 1 小於 3(再次向左走)。添加值 10 後,它成為根的右孩子,因為 10 大於 8。這個過程繼續使用值 6、4、7、14 和 13。這個二叉搜索樹的深度為 3,這意味著距離根最遠的值是三個節點。

二叉搜索樹自然會以排序順序結束,因此對於快速查找數據很有用,因為您可以立即消除每一步的可能性。通過限制需要調查的節點數量,可以更快地完成搜索。假設您想在上面的樹中找到值 6。從根開始,您確定 6 小於 8,因此前往根的左孩子。由於 6 大於 3,因此您前往正確的節點。這就是您正在尋找的價值。所以不用訪問九個節點來找到這個值,你只需要訪問三個。

要在 JavaScript 中構建二叉搜索樹實現,第一步是定義基本接口:

function BinarySearchTree() {
    this._root = null;
}

BinarySearchTree.prototype = {

    //restore constructor
    constructor: BinarySearchTree,

    add: function (value){
    },

    contains: function(value){
    },

    remove: function(value){
    },

    size: function(){
    },

    toArray: function(){
    },

    toString: function(){
    }

};

基本接口類似於其他數據結構,具有添加和刪除值的方法。我還添加了一些方便的方法,size() , toArray() , 和 toString() ,這對 JavaScript 很有用。

要掌握使用二叉搜索樹,最好的方法是開始使用 contains() . contains() 方法接受一個值作為參數並返回 true 如果值存在於樹中或 false 如果不。該方法遵循基本的二分查找算法來判斷值是否存在:

BinarySearchTree.prototype = {

    //more code

    contains: function(value){
        var found       = false,
            current     = this._root

        //make sure there's a node to search
        while(!found && current){

            //if the value is less than the current node's, go left
            if (value < current.value){
                current = current.left;

            //if the value is greater than the current node's, go right
            } else if (value > current.value){
                current = current.right;

            //values are equal, found it!
            } else {
                found = true;
            }
        }

        //only proceed if the node was found
        return found;
    },

    //more code

};

搜索從樹的根開始。如果沒有添加數據,則可能沒有根,因此必須對此進行檢查。遍歷樹遵循前面討論的簡單算法:如果要查找的值小於當前節點,則向左,如果值更大,則向右。 current 每次都覆蓋指針,直到找到值(在這種情況下 found 設置為 true ) 或者沒有更多的節點可以在那個方向搜索(在這種情況下,值不在樹中)。

contains()中使用的方法 也可用於向樹中插入新值。主要區別在於您將尋找放置新值的位置,而不是在樹中查找值:

BinarySearchTree.prototype = {

    //more code

    add: function(value){
        //create a new item object, place data in
        var node = {
                value: value,
                left: null,
                right: null
            },

            //used to traverse the structure
            current;

        //special case: no items in the tree yet
        if (this._root === null){
            this._root = node;
        } else {
            current = this._root;

            while(true){

                //if the new value is less than this node's value, go left
                if (value < current.value){

                    //if there's no left, then the new node belongs there
                    if (current.left === null){
                        current.left = node;
                        break;
                    } else {
                        current = current.left;
                    }

                //if the new value is greater than this node's value, go right
                } else if (value > current.value){

                    //if there's no right, then the new node belongs there
                    if (current.right === null){
                        current.right = node;
                        break;
                    } else {
                        current = current.right;
                    }       

                //if the new value is equal to the current one, just ignore
                } else {
                    break;
                }
            }
        }
    },

    //more code

};

將值添加到二叉搜索樹時,特殊情況是還沒有根。在這種情況下,工作很簡單,因為您只需將根設置為新值。對於所有其他情況,基本算法與 contains() 中使用的算法完全相同 :向左是新值小於當前節點,如果值更大則向右。主要區別在於,當您無法再進一步時,那就是新值的位置。因此,如果您需要向左但沒有左節點,則新值將成為左節點(與右節點相同)。由於不能重複,如果找到具有相同值的節點,則操作停止。

在轉到 size() 之前 方法,我想離題討論一下樹的遍歷。為了計算二叉搜索樹的大小,需要訪問樹中的每個節點。二叉搜索樹通常需要執行不同類型的遍歷來檢索信息,最常用的是中序遍歷。通過處理左子樹,然後是節點本身,然後是右子樹,對每個節點執行按順序遍歷。由於二叉搜索樹以這種方式排序,從左到右,結果是以正確的排序順序處理節點。對於 size() 方法,實際上遍歷節點的順序並不重要,但對於 toArray() 確實很重要 方法。由於這兩種方法都需要進行遍歷,所以我決定添加一個 traverse() 可以通用的方法:

BinarySearchTree.prototype = {

    //more code

    traverse: function(process){

        //helper function
        function inOrder(node){
            if (node){

                //traverse the left subtree
                if (node.left !== null){
                    inOrder(node.left);
                }            

                //call the process method on this node
                process.call(this, node);

                //traverse the right subtree
                if (node.right !== null){
                    inOrder(node.right);
                }
            }
        }

        //start with the root
        inOrder(this._root);
    },

    //more code

};

此方法接受單個參數 process ,這是一個應該在樹中的每個節點上運行的函數。該方法定義了一個名為 inOrder() 的輔助函數 用於遞歸遍歷樹。請注意,如果該節點存在,遞歸只會左右移動(以避免處理 null 多次)。 traverse() 方法然後從根節點和 process() 開始按順序遍歷 函數處理處理每個節點。然後可以使用此方法來實現 size() , toArray() ,以及傳遞性地,toString()

BinarySearchTree.prototype = {

    //more code

    size: function(){
        var length = 0;

        this.traverse(function(node){
            length++;
        });

        return length;
    },

    toArray: function(){
        var result = [];

        this.traverse(function(node){
            result.push(node.value);
        });

        return result;
    },

    toString: function(){
        return this.toArray().toString();
    },

    //more code

};

size()toArray() 調用traverse() 方法並傳入一個函數以在每個節點上運行。 size()的情況 ,該函數只是增加長度變量,而 toArray() 使用該函數將節點的值添加到數組中。 toString() 然後方法調用 toArray() 在將返回的數組轉換為字符串並返回之前。

在本文的第 2 部分中,將討論從二叉搜索樹中刪除節點。刪除是一個複雜的問題,需要考慮很多情況,因此需要自己編寫。同時,您可以在我的計算機科學中的 JavaScript GitHub 項目中獲得完整的源代碼。


Tutorial JavaScript 教程
  1. 如何在字符串中查找短語?

  2. 初學者遇到問題的解決方案~React/Deploy~

  3. React.Js 中的狀態管理

  4. 構建 todometer:基於儀表的待辦事項列表

  5. 角度應用程序的每個性能提示

  6. 高級 TypeScript 練習 - 問題 4

  7. Uncaught TypeError:$ is not a function at (index):2

  1. PWA,和原生應用一樣好嗎?

  2. 我寫的一些個人小程序

  3. JavaScript 按箭頭函數排序值

  4. 基準測試:Apollo Federation Gateway v1 vs v2 vs WunderGraph vs mercurius-js

  5. 如何使用航點進行單頁導航以啟用/禁用導航元素?

  6. Node js如何每2秒運行一次axios.get?

  7. 純javascript內置的天氣應用程序

  1. 使用CSS3單擊按鈕上的波紋效果動畫

  2. 使用 Vercel 創建無服務器函數

  3. 哦,一個 SIGTERM 信號!

  4. 在 Docker 中運行 Node.js 以進行本地開發