二叉搜索树

250 阅读3分钟

二叉搜索树的定义

二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,二叉搜索树(Binary Search Tree),又名二叉排序树(Binary Sort Tree)

二叉搜索树是具有有以下性质的二叉树:

  • 若左子树不为空,则左子树上所有节点的值均小于或等于它的根节点的值
  • 若右子树不为空,则右子树上所有节点的值均大于或等于它的根节点的值
  • 左、右子树也分别为二叉搜索树

二叉搜索树的相关操作

function BinarySearchTree(){
    function Node(key){
        this.key = key;
        this.left = null;
        this.right = null;
    }
    
    // 初始化根节点
    this.root = null;
    
    // 插入方法 对外暴露出来的添加函数
    BinarySearchTree.prototype.insert = function(key) {
        // 先创建一个
        let newNode = new Node(key);
        
        if (this.root == null) {
            this.root = newNode
        }else{
            this.insertNode(this.root, newNode);
        }
    }
    
    // 内部插入函数
    BinarySearchTree.prototype.insertNode = function (node, newNode) {
        if(newNode.key < node.key){ // 向左查找
            if(node.left === null){
                node.left = newNode;
            }else{// 说明左子树有值
                this.insertNode(node.left, newNode);
            }
        }else{
            if (node.right == null) { // 右子树不存在 
                node.right = newNode;
            }else{ // 说明右子树不为空,继续找合适位置
                this.insertNode(node.right, newNode);
            }
        }
    }
    
    /*树的遍历*/
    // 1.先序遍历 先处理节点本身,然后再处理它的左子树,最后处理右子树,所以称之为先序遍历
    BinarySearchTree.prototype.preOrderTraversal = function(handler) {
        this.preOrderTraversalNode(this.root, handler);
    }
    BinarySearchTree.prototype.midOrderTraversalNode = function(node, handler){
        if (node != null) {
            // 1.处理经过的节点
            handler(node.key);
            
            // 2.处理左边节点 左子树处理完成后,递归的压栈结束,然后从栈中移除,接着向下执行,遍历它的右子树,如果右子树遍历完后,然后向上回溯,这样就是递归的原理和执行过程
            this.preOrderTraversalNode(node.left, handler);
            
            // 3.处理右边节点
            this.preOrderTraversalNode(node.right, handler);
        }
    }
    
    /*
    2.中序遍历
        1>中序遍历其左子树
        2>访问根节点
        3>中序遍历其右子树
    */ 
    BinarySearchTree.prototype.midOrderTraversal = function(handler){
        this.midOrderTraversalNode(this.root, handler);
    }
    BinarySearchTree.prototype.midOrderTraversalNode = function(node, handler){
        if (node != null) {
            // 1.遍历左子树
            this.midOrderTraversalNode(node.left, handler);
            
            // 2.处理根节点
            handler(node.key);
            
            // 3.遍历右子树
            this.midOrderTraversalNode(node.right, handler);
        }
    }
    
    /*
    3.后序遍历
        1>后序遍历其左子树
        2>后序遍历其右子树
        3>访问根节点
    */
    BinarySearchTree.prototype.postOrderTraversal = function(handler){
        this.postOrderTraversalNode(this.root, handler);
    }
    BinarySearchTree.prototype.postOrderTraversalNode = function(node, handler){
        if (node != null) {
            // 1.遍历左子树
            this.postOrderTraversalNode(node.left, handler);

            // 2.遍历右子树
            this.postOrderTraversalNode(node.right, handler);

            // 3.处理根节点
            handler(node.key);
        }
    }
    
    // 查找树中的最大值
    BinarySearchTree.prototype.max = function(){
        let currentNode = this.root;
        while(currentNode.right){
            currentNode = currentNode.right;
        }
        
        return currentNode.key
    }
    
    // 查找树中的最小值
    BinarySearchTree.prototype.min = function(){
        let currentNode = this.root;
        while(currentNode.left){
            currentNode = current.left;
        }
        
        return currentNode.key;
    }
    
    // 搜索key是否在树中
    BinarySearchTree.prototype.search = function(key){
        return this.searchNode(this.root, key)
    }
    BinarySearchTree.prototype.searchNode = function(node, key){
        if(node == null){
            return false
        }
        
        if(node.key > key){ // 从节点的左子树遍历查找
            this.searchNode(node.left, key);
        }else if(node.key > key){ // 从节点的右子树遍历查找
            this.searchNode(node.right, key);
        }else{
            return true;
        }
    }

    // 删除节点
    BinarySearchTree.prototype.remove = function(key){
        // 1.寻找要删除的节点
        // 1.1保存一些变量
        let current = this.root;
        let parent = null;
        let isLeftChild = true;
        
        // 1.2遍历查找
        while(node.key != key){
            parent = current;
            if(key < current.key) { //从左边找
                current = current.left;
                isLeftChild = true;
            }else{
                current = current.right;
                isLeftChild = false;
            }
            
            // 最后还是没有找到
            if(current == null) return false;
        }
        
        // 2.找到要删除的元素
        // 2.1删除的节点是叶子节点
        if (current.left == null && current.right == null) {
            if(current == this.root){ //仅有一个节点,同时它是根节点
                this.root =  null
            }else{
                if(isLeftChild){
                    parent.left = null;
                }else{
                    parent.right = null;
                }
            }
        }else if(current.right == null){ // 2.2删除的节点有一个节点
            if (current == this.root) { // 如果删除的节点是在根节点下面的,所以需要添加一层判断
                this.root = current.left;
            } else if (isLeftChild) {
                parent.left = current.left;
            }else{
                parent.right = current.left;
            }
        }else if(current.left == null){ // 如果删除的节点是在根节点下面的,所以需要添加一层判断
            if (current == this.root) {
                this.root = current.right;
            } else if (isLeftChild) {
                parent.left = current.right
            }else{
                parent.right = current.right;
            }
        } else{
            // 2.3删除的节点有两个节点
            // 待删除节点既有左子树也有右子树:找到该节点右子树中最小值节点,使用该节点代替待删除节点,然后在右子树中删除最小值节点。
            let rightNode = current.right;
            let tempParent = current; // 记录最小节点的父节点,方便删除
            
            while (rightNode.left) {
                tempParent = rightNode;
                rightNode = rightNode.left;
            }
            
            // 2.3.1 使用该节点代替待删除节点
            if(current == this.root){
                this.root = rightNode;
            }else{
                if(isLeftChild){
                    parent.left = rightNode;
                }else{
                    parent.right = rightNode;
                }
            }
            
            // 把删除节点的左右节点分别赋值给新节点
            if (rightNode != current.right) {
                rightNode.right = current.right;
            }
            if(rightNode != current.left){
                rightNode.left = current.left;
            }
            
            // 2.3.2 右子树中删除最小值节点
            if(current != this.root){
                tempParent.left = null;
            }
        }
    }
    
    // 反转二叉树的操作
    BinarySearchTree.prototype.invertTree = function(){
        this.invertTreeNode(this.root);
    }
    // 递归调用
    BinarySearchTree.prototype.invertTreeNode = function(root) {
        // 逢空返回null
        if(!root) return null;
        // 节省时间不用再去反转最后的子节点
        if(root.left || root.right){
            let temp = root.left;
            root.left = root.right;
            root.right = temp;
            
            // 递归遍历左右子树
            this.invertTreeNode(root.left)
            this.invertTreeNode(root.right)
        }
        
        return root;
    }
}