javaScriptはバランスツリーを実現します.

21461 ワード

const Compare ={
    LESS_THAN:-1,
    BIGGER_THAN:1,
    EQUALS:0
}
class Node{
    constructor(key){
        this.key = key;
        this.left = null;
        this.right = null;
    }
}
function defaultCompare(a,b){
    return a == b ? Compare.EQUALS:(a < b) ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
class BinarySearchTree{
    constructor(comPareFn = defaultCompare){
        this.root = null;
        this.comPareFn = comPareFn;
    }

    insert(key){
        if(this.root == null){
            this.root = new Node(key);
        }else{
            this.insertNode(this.root,key);
        }
        
    }
    insertNode(node,key){
        if(this.comPareFn(key,node.key) === Compare.LESS_THAN){
            if(node.left == null){
                node.left = new Node(key);      
            }else{
                this.insertNode(node.left,key);
            }
        }else{
            if(node.right == null){
                node.right = new Node(key);
            }else{
                this.insertNode(node.right,key);
            }
        }
    }
    search(key){
        return this.searchNode(this.root,key);
    }
    searchNode(node,key){
        if(node == null){
            return false;
        }
        if(this.comPareFn(key,node.key) === Compare.LESS_THAN){
            return this.searchNode(node.left,key);
        }
        else if(this.comPareFn(key,node.key) === Compare.BIGGER_THAN){
            return this.searchNode(node.right,key);
        }else{
            return true;
        }
    }
    min(){
        return this.minNode(this.root);
    }
    minNode(node){
        if(node == null){
            return node;
        }
        while(node.left!=null){
            node = node.left;
        }
        return node;
    }
    max(){
        return this.maxNode(this.root);
    }
    maxNode(node){
        if(node == null){
            return node;
        }
        while(node.right != null){
            node = node.right;
        }
        return node;
    }
    inOrderTraverse(callback){
       this.inOrderTraverseNode(this.root,callback);
    }
    inOrderTraverseNode(node,callback){
        if(node == null){
            return;
        }
        this.inOrderTraverseNode(node.left,callback);
        callback(node.key);
        this.inOrderTraverseNode(node.right,callback);
    }
    preTraverse(callback){
        this.preTraverseNode(this.root,callback);
    }
    preTraverseNode(node,callback){
        if(node == null){
            return;
        }
        callback(node.key);
        this.preTraverseNode(node.left,callback);
        this.preTraverseNode(node.right,callback);
    }
    postTraverse(callback){
        this.postTraverseNode(this.root,callback);
    }
    postTraverseNode(node,callback){
        if(node == null){
            return;
        }
        this.postTraverseNode(node.left,callback);
        this.postTraverseNode(node.right,callback);
        callback(node.key);
    }
    remove(key){
        this.root =  this.removeNode(this.root,key);
    }
    removeNode(node,key){
        if(node == null){
            return false;
        }
        if(this.comPareFn(key,node.key) === Compare.LESS_THAN){
            node.left =  this.removeNode(node.left,key);
            return node;
        }else if(this.comPareFn(key,node.key) === Compare.BIGGER_THAN){
            node.right =  this.removeNode(node.right,key);
            return node;
        }else{
            if(node.left == null && node.right == null){
                node = null;
                return node;
            }
            if(node.left == null){
                node = node.right;
                return node;
            }
            if(node.right == null){
                node = node.left;
                return node;
            }
            let tmp = this.minNode(node.right);
            node.key = tmp.key;
            node.right = this.removeNode(node.right,tmp.key);
            return node;
        }
    }
}

const BalanceFactor ={
    UNBALANCED_RIGHT:1,
    SLIGHTLY_UNBALANCED_RIGHT:2,
    BALANCED:3,
    UNBALANCED_LEFT:4,
    SLIGHTLY_UNBALANCED_LEFT:5
}

const printNode = (value) => console.log(value);
class AVLTree extends BinarySearchTree{
    constructor(comPareFn = defaultCompare){
        super(comPareFn);
    }
    getNodeHeight(node){
        if(node == null){
            return -1;
        }
        return Math.max(
            this.getNodeHeight(node.left),this.getNodeHeight(node.right)
            )+1;
     }
     getBalanceFactor(node){
        const factor = this.getNodeHeight(node.left) - this.getNodeHeight(node.right);
        switch(factor){
            case 1:
               return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT;
            case 2:
               return BalanceFactor.UNBALANCED_LEFT;
            case -1:
               return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT;
            case -2:
               return BalanceFactor.UNBALANCED_RIGHT;
            default:
               return BalanceFactor.BALANCED;
        }
    }
    insert(key){
        this.root = this.insertNode(this.root,key);
    }
    insertNode(node,key){
        if(node == null){
            return new Node(key);
        }
        if(this.comPareFn(key,node.key) === Compare.LESS_THAN){
            node.left = this.insertNode(node.left,key);
        }
        else if(this.comPareFn(key,node.key) === Compare.BIGGER_THAN){
            node.right = this.insertNode(node.right,key);
        }else{
            return node;
        }
        const balanceFactor = this.getBalanceFactor(node);
        if(balanceFactor === BalanceFactor.UNBALANCED_LEFT){
            if(this.comPareFn(key,node.key) === Compare.LESS_THAN){
                node =  this.rotationLL(node);
            }else{
                return this.rotationLR(node);
            }
        }else if(balanceFactor === BalanceFactor.UNBALANCED_RIGHT){
            if(this.comPareFn(key,node.key) === Compare.BIGGER_THAN){
                node =  this.rotationRR(node);
            }else{
                return this.rotationRL(node);
            }
        }
        return node;
    }
    rotationLL(node){
        const tmp = node.left;
        node.left = tmp.right;
        tmp.right = node;
        node = tmp;
        return tmp;
    }
    rotationRR(node){
        const tmp = node.right;
        node.right = tmp.left;
        tmp.left = node;
        return tmp;
    }
    rotationLR(node){
        node.left = this.rotationLL(node.left);
        return this.rotationRR(node);
    }
    rotationRL(node){
        node.right = this.rotationRR(node.right);
        return this.rotationLL(node);
    }
    remove(key){
        this.root = this.removeNode(this.root,key);
    }
    removeNode(node,key){
        node = super.removeNode(node,key);
        if(node == null){
            return node;
        }
        let balanceFactor = this.getBalanceFactor(node);
        if(balanceFactor === BalanceFactor.UNBALANCED_LEFT){
            let balanceFactor = this.getBalanceFactor(node.left);
            if(balanceFactor == BalanceFactor.SLIGHTLY_UNBALANCED_LEFT || balanceFactor == BalanceFactor.BALANCED){
                return this.rotationLL(node);
            }
            if(balanceFactor == BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT){
                return this.rotationLR(node.left);
            }
        }
        if(balanceFactor === BalanceFactor.UNBALANCED_RIGHT){
            let balanceFactor = this.getBalanceFactor(node.left);
            if(balanceFactor == BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT || balanceFactor == BalanceFactor.BALANCED){
                return this.rotationRR(node);
            }else if(balanceFactor == BalanceFactor.SLIGHTLY_UNBALANCED_LEFT){
                return this.rotationRL(node.right);
            }
        }
        return node;
    }
}