用JS實現二叉搜索樹


二叉樹的節點最多只能有兩個子節點,一個左側子節點,一個右側子節點。

二叉搜索樹(BST),是二叉樹的一種,但只允許在左側節點存儲比父節點小的值,在右側節點存儲比父節點大或等於父節點的值。

1.創建BST

1.1創建BST類

首先申明BST類的基本結構

function BinarySearchTree() {
    var Node = function(key){
        this.key = key;
        this.left = null;
        this.right = null;
    };

    var root = null;
}

下面我們實現一些基本方法

1.2 向樹中插入一個鍵

插入一個鍵時,有三步:第一步,創建一個表示新節點的Node類實例;第二步,判斷是否有根節點,如果沒有,則插入節點為根節點;第三步,如果已經有根節點,需要一個輔助方法來判斷新節點的

插入位置,方法如下

var insertNode = function(node,newNode){
        if (newNode.key < node.key) {
            if (node.left === null) {
                node.left = newNode;
            }
            else{
                insertNode(node.left,newNode);
            }
        }
        else{
            if (node.right === null) {
                node.right = newNode;
            }
            else{
                insertNode(node.right,newNode);
            }
        }
    }

 以上方法的意思是,先用新節點和根節點作對比,判斷根節點左側(新節點小於根節點)或右側(新節點大於根節點)子節點是否有值,如果沒有則插入,如果有,則繼續用新節點和根節點的子節點比

大小,做遞歸操作,逐步得到新節點插入位置,下面是插入節點的方法

this.insert = function(key){
        var newNode = new Node(key);

        if (root === null) {
            root = newNode;
        }
        else{
            insertNode(node.left, newNode);
        }
    }

 

1.3 遍歷樹

遍歷一棵樹是指訪問樹的每一個節點,並對它們進行某種操作的過程。訪問樹的節點有三種方法,中序,先序和后序。

 

1.3.1 中序遍歷

以最小到最大的順序訪問所有的節點

var inOrderTraverseNode = function(node,callback){
        if (node !== null) {
            inOrderTraverseNode(node.left,callback);
            callback(node.key);
            inOrderTraverseNode(node.right,callback);
        }
    }
this.inOrderTraverse = function(callback){
        inOrderTraverseNode(root,callback);
    }

以上代碼中,inOrderTraverse方法接受一個回掉函數作為參數,來定義對遍歷到的每一個節點的操作,這個方法中調用了一個輔助方法inOrderTraverseNode,用來遞歸遍歷所有值,當遞歸到最后一級

時,觀察輔助函數內部,調用回掉函數依次是左側子節點,父節點,右側子節點,所以執行回調函數順序為按照節點值從小到大。

 

1.3.2 先序遍歷

祖先節點優先於后代節點的順序遍歷

var preOrderTraverseNode = function(node,callback){
        if (node !== null) {
            callback(node.key);
            preOrderTraverseNode(node.left,callback);
            preOrderTraverseNode(node.right,callback);
        }
    };

this.preOrderTraverse = function(callback){
        preOrderTraverseNode(root,callback);
    }

以上代碼中,preOrderTraverse方法接受一個回掉函數作為參數,來定義對遍歷到的每一個節點的操作,這個方法中調用了一個輔助方法preOrderTraverseNode,用來遞歸遍歷所有值,當遞歸到最后一級

時,觀察輔助函數內部,調用回掉函數依次是父節點,左側子節點,右側子節點,所以遍歷優先祖先節點。

1.3.3 后序遍歷

先訪問節點的后代節點,在訪問節點本身

var postOrderTraverseNode = function(node,callback){
        if (node !== null) {
            callback(node.key);
            postOrderTraverseNode(node.left,callback);
            postOrderTraverseNode(node.right,callback);
        }
    };
this.postOrderTraverse = function(callback){
        postOrderTraverseNode(root,callback);
    }

以上代碼中,postOrderTraverse方法接受一個回掉函數作為參數,來定義對遍歷到的每一個節點的操作,這個方法中調用了一個輔助方法postOrderTraverseNode,用來遞歸遍歷所有值,當遞歸到最后一級

時,觀察輔助函數內部,調用回掉函數依次是左側子節點,右側子節點,父節點,所以遍歷優先子節點。

 

1.4 搜索樹中的值

1.4.1 最大值最小值

最大值為搜索樹右下角的值,最小值為搜索樹左下角的值,下面來看實現

1.最大值

var maxNode = function(node){
        if (node) {
            while(node&&node.right !== null){
                node = node.right;
            }
            return node.key;
        }
        return null;
    };
this.max = function(){
        return maxNode(root);
    }

2.最小值

var minNode = function(node){
        if (node) {
            while(node&&node.left !== null){
                node = node.left;
            }
            return node.key;
        }
        return null;
    };
this.min = function(){
        return minNode(root);
    }

實現最大值最小值,同樣的方法,從根節點開始循環,直到右下角或左下角的值,返回回來。

 

1.4.2 搜索任意值
var searchNode = function(node,key){
        if (node === null) {
            return false;
        }
        if (key<node.key) {
            return searchNode(node.left,key)
        }
        else if (key>node.key) {
            return searchNode(node.right,key)
        }
        else{
            return true;
        }
    };
this.search = function(key){
        return searchNode(root,key);
    }

邏輯其實很簡單,拿給定的節點從根節點開始查找,比根節點大,則繼續與根節點右側子節點比較,比根節點大,則繼續與根節點左側子節點比較,依次遞歸,直到給定節點與某節點相等,表示找到,

返回true,如果沒找到,返回false

 

1.4.3 刪除一個節點
var removeNode = function(node,key){
        if (node === null) {
            return null;
        }
        if (key<node.key) {
            node.left = remove(node.left,key)
        }
        else if (key>node.key) {
            node.left = remove(node.right,key)
        }
        // 鍵等於node.key
        else{
            //葉節點
            if (node.left === null&&node.right === null) {
                node = null;
                return node;
            }
            // 只有一個子節點的節點
            if (node.left === null) {
                node = node.right;
                return node;
            }
            else if(node.right === null) {
                node = node.left;
                return node;
            }
            // 有兩個子節點的節點
            var aux = findMinNode(node.right);
            node.key = aux.key;
            node.right = removeNode(node.right,aux.key);
            return node;
        }
    }
this.remove =function(key){
        root = removeNode(root,key);
    }

 上面當刪除節點有兩個子節點時,需要調用輔助方法findMinNode

var minNodeSelf = function(node){
        if (node) {
            while(node&&node.left !== null){
                node = node.left;
            }
            return node;
        }
        return null;
    };
this.findMinNode = function(){
        return minNodeSelf(node);
    },

下面是實現BST的完整代碼

function BinarySearchTree() {
    var Node = function(key){
        this.key = key;
        this.left = null;
        this.right = null;
    };

    var root = null;

    var insertNode = function(node,newNode){
        if (newNode.key < node.key) {
            if (node.left === null) {
                node.left = newNode;
            }
            else{
                insertNode(node.left,newNode);
            }
        }
        else{
            if (node.right === null) {
                node.right = newNode;
            }
            else{
                insertNode(node.right,newNode);
            }
        }
    };

    var inOrderTraverseNode = function(node,callback){
        if (node !== null) {
            inOrderTraverseNode(node.left,callback);
            callback(node.key);
            inOrderTraverseNode(node.right,callback);
        }
    };

    var preOrderTraverseNode = function(node,callback){
        if (node !== null) {
            callback(node.key);
            preOrderTraverseNode(node.left,callback);
            preOrderTraverseNode(node.right,callback);
        }
    };

    var postOrderTraverseNode = function(node,callback){
        if (node !== null) {
            callback(node.key);
            postOrderTraverseNode(node.left,callback);
            postOrderTraverseNode(node.right,callback);
        }
    };

    var maxNode = function(node){
        if (node) {
            while(node&&node.right !== null){
                node = node.right;
            }
            return node.key;
        }
        return null;
    };

    var minNode = function(node){
        if (node) {
            while(node&&node.left !== null){
                node = node.left;
            }
            return node.key;
        }
        return null;
    };

    var minNodeSelf = function(node){
        if (node) {
            while(node&&node.left !== null){
                node = node.left;
            }
            return node;
        }
        return null;
    };

    var searchNode = function(node,key){
        if (node === null) {
            return false;
        }
        if (key<node.key) {
            return searchNode(node.left,key)
        }
        else if (key>node.key) {
            return searchNode(node.right,key)
        }
        else{
            return true;
        }
    };

    var removeNode = function(node,key){
        if (node === null) {
            return null;
        }
        if (key<node.key) {
            node.left = remove(node.left,key)
        }
        else if (key>node.key) {
            node.left = remove(node.right,key)
        }
        // 鍵等於node.key
        else{
            //葉節點
            if (node.left === null&&node.right === null) {
                node = null;
                return node;
            }
            // 只有一個子節點的節點
            if (node.left === null) {
                node = node.right;
                return node;
            }
            else if(node.right === null) {
                node = node.left;
                return node;
            }
            // 有兩個子節點的節點
            var aux = findMinNode(node.right);
            node.key = aux.key;
            node.right = removeNode(node.right,aux.key);
            return node;
        }
    };

    this.insert = function(key){
        var newNode = new Node(key);

        if (root === null) {
            root = newNode;
        }
        else{
            insertNode(node.left, newNode);
        }
    },

    this.inOrderTraverse = function(callback){
        inOrderTraverseNode(root,callback);
    },

    this.preOrderTraverse = function(callback){
        preOrderTraverseNode(root,callback);
    },

    this.postOrderTraverse = function(callback){
        postOrderTraverseNode(root,callback);
    },

    this.max = function(){
        return maxNode(root);
    },

    this.min = function(){
        return minNode(root);
    },

    this.findMinNode = function(){
        return minNodeSelf(root);
    },

    this.search = function(key){
        return searchNode(root,key);
    },

    this.remove =function(key){
        root = removeNode(root,key);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM