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; } }