用JS实现二叉搜索树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用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);
    }
}

 

以上是关于用JS实现二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章

用JS实现二叉树

js 数据结构 树(二叉搜索树的实现)

为啥用二叉搜索树实现哈希表?

二叉搜索树 Rust实现

数据结构-二叉搜索树的js实现

用js来实现那些数据结构14(树02-AVL树)