数据结构-JS实现二叉排序树

230 阅读4分钟

二叉排序树

二叉排序树(Binary Search Tree),(又:二叉搜索树,二叉查找树)它或者是一棵空树,或者是具有下列性质的二叉树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树。
    二叉搜索树作为一种经典的数据结构,具有快速插入与删除的特点,又有快速查找的优势。

对二叉排序树的基本操作:

  • 添加元素(insert)
  • 获取最大元素(getMin)
  • 获取最大元素(getMax)
  • 查找某个元素是否存在于树中(find)
  • 删除某个元素(remove)
  • 输出树(toString)

添加节点

通过insert方法向树添加节点。

  1. 首先检查BST是否有根节点,如果没有,说明是一个空树,该节点就是根节点。否则进入下一步。
  2. 设置根节点为当前节点。
  3. 若待插入节点的数据小于当前节点,则设当前节点的左节点为新的当前节点。否则进入第5步。
  4. 若当前节点的左节点为null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。
  5. 若待插入节点的数据大于当前节点,则设当前节点的右节点为新的当前节点。
  6. 若当前节点的右节点为null,就将新的节点插入这个位置,退出循环;反之,继续执行下一次循环。

删除节点

通过remove方法删除节点。

  1. 首先设置根节点为当前节点。
  2. 判断当前节点是否包含待删除的数据,如果包含,则删除该节点;否则比较当前节点上的数据和待删除的数据。
  3. 如果待删除的数据小于当前节点的数据,则移动至当前节点的左子节点继续比较;如果待删除数据大于当前节点的数据,则移动至当前节点的右子节点继续比较。

删除节点有三种情况:

  • 待删除节点是叶子节点。只需要将从父节点指向它的链接指向null。
  • 待删除节点只包含一个子节点。只需要将从父节点指向它的链接指向指向待删除节点的子节点。
  • 待删除节点包含两个子节点。正确的做法有两种:要么查找待删除节点左子树上的最大值,要么查找其右子树上的最小值。本文的示例代码选择的是后一种方式。我们需要一个查找子树上最小值的方法getRightChildSmallest(),会用它找到的最小值创建一个临时节点。将临时节点上的值复制到待删除节点,然后再删除子树上的临时节点。

/*
 * 节点类
 * Node对象存储数据,保存和其他节点的链接(left和right),show()方法用来显示保存在节点中的数据。
 */
class Node{
    constructor(data,left,right){
        this.data = data;
        this.left = left;
        this.right = right;
    }
    show(){
        return this.data;
    }
}


class BST{
    constructor(){
        this.root = null;
    }
    insert(data){
        let node = new Node(data,null,null);
        if(this.root === null){
            this.root = node;
        }else{
            let current = this.root, parent = null;
            while(true){
                parent = current;
                if(data < current.data){
                    current = current.left;
                    if(current === null){
                        parent.left = node;
                        break;
                    }
                }else{
                    current = current.right;
                    if(current === null){
                        parent.right = node;
                        break;
                    }
                }
            }
        }
    }
    //中序遍历
    toString(node){
        node = node===undefined ? this.root : node;
        let str = "";
        if(node !== null){
            str = str + this.toString(node.left);
            str = str + node.show()+" ";
            str = str + this.toString(node.right);
        }
        return str;
    }
    getMin(){
        let current = this.root;
        while(current.left !== null ){
            current = current.left;
        }
        return current.show();
    }
    getMax(){
        let current = this.root;
        while(current.right !== null ){
            current = current.right;
        }
        return current.show();
    }
    find(data){
        let current = this.root;
        while(current !== null){
            if(current.data === data){
                return true;
            }else if(current.data < data){
                current = current.right;
            }else{
                current = current.left;
            }
        }
        return false;
    }
    getRightChildSmallest(node){
        let current = node.right;
        while(current.left !== null){
            current = current.left;
        }
        return current;
    }
    remove(data){
        this.removeTmp(this.root,data);
    }
    removeTmp(node, data){
        if(node === null){
            return null;
        }
        
        if(node.data === data){
            if(node.left === null && node.right === null){ //判断是否是叶子节点
                return null;
            }else if(node.left === null){//判断是否只有右子节点
                return node.right;
            }else if(node.right === null){//判断是否只有左子节点
                return node.left;
            }
            //存在两个子节点,查找其右子树上的最小值
            let tmpNode = this.getRightChildSmallest(node);
            node.data = tmpNode.data;
            node.right = this.removeTmp(node.right,tmpNode.data);   //再删除子树上的临时节点
            return node;
        }else if(node.data < data){
            node.right = this.removeTmp(node.right,data);
            return node;
        }else{
            node.left = this.removeTmp(node.left,data);
            return node;
        }
    }
}

let bst = new BST();
bst.insert(23);
bst.insert(4);
bst.insert(78);
bst.insert(3);
bst.insert(9);
bst.insert(16);
bst.insert(47);
bst.insert(24);
console.log(bst.toString());            //3 4 9 16 23 24 47 78
bst.remove(23)
console.log(bst.toString());            //3 4 9 16 24 47 78

应用

使用二叉排序树对数组进行排序。

let arr = [12,97,58,39,27,19,69,47,36];
let tmp = new BST();
for(let i = 0; i < arr.length; i++){
	tmp.insert(arr[i]);
}
console.log(tmp.toString());            //12 19 27 36 39 47 58 69 97