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