版权声明:转载请注明出处 https://blog.csdn.net/wushichao0325/article/details/86164572
简介
AVL树是一种自平衡树。添加或移除节点时, AVL树会尝试自平衡。任意一个节点(不论深
度)的左子树和右子树高度最多相差1。添加或移除节点时, AVL树会尽可能尝试转换为完全树。
平衡因子是在AVL树中,需要对每个节点计算右子树高度( hr)和左子树高度( hl)的差值,该值( hr- hl)应为0、1或-1。如果结果不是这三个值之一,则需要平衡该AVL树。
计算平衡因子:
this[heightNode]=function(node){
if(node==null){
return -1;
}else{
return Math.max(this[heightNode](node.left),this[heightNode](node.right))+1;
}
}
AVL旋转是向AVL树插入节点时,可以执行单旋转或双旋转两种平衡操作,分别对应四种场景:
- 右-右( RR):向左的单旋转
- 左-左( LL):向右的单旋转
- 左-右( LR):向右的双旋转
- 右-左( RL):向左的双旋转
RR:
this[rotationRR]=function(node){//当插入的节点在右右节点
let tmp=node.right;
node.right=tmp.left;
tmp.left=node;
return tmp;
}
LL:
this[rotationLL]=function(node){//当插入的节点在左左节点
let tmp=node.left;
node.left=tmp.right;
tmp.right=node;
return tmp;
}
LR:
this[rotationLR]=function(node){//当插入的节点在左右节点
node.left=this[rotationRR](node.left);//向左单旋转
return this[rotationLL](node);//再向右单旋转
}
RL:
this[rotationRL]=function(node){//当插入的节点在右左节点
node.right=this[rotationLL](node.right);//向右单旋转
return this[rotationRR](node);//再向左单旋转
}
代码实现
let insertNode=Symbol();//伪私有函数
let inOrderTraverseNode=Symbol();//中序遍历
let preOrderTraverseNode=Symbol();//先序遍历
let postOrderTraverseNode=Symbol();//后序遍历
let minNode=Symbol();//最小值节点
let maxNode=Symbol();//最大值节点
let searchNode=Symbol();//搜索特定的值
let removeNode=Symbol();//移除一个节点
let findMinNode=Symbol();//搜索某个节点的最小值
let heightNode=Symbol();//计算节点高度
let rotationRR=Symbol();//向左单旋转
let rotationLL=Symbol();//向右单旋转
let rotationLR=Symbol();//向右双旋转
let rotationRL=Symbol();//向左双旋转
class Node{
constructor(key){
this.key=key;
this.left=null;
this.right=null;
}
}
class AVLTree{//自平衡二叉树
constructor(){
this.root=null;
this[insertNode]=function(node,newNode){
if(newNode.key<node.key){
if(node.left==null){
node.left=newNode;
}else{
node.left=this[insertNode](node.left,newNode);
if(node.left!=null){//判断加入到左支后是否平衡
//确认是否需要平衡
if((this[heightNode](node.left)-this[heightNode](node.right))>1){
//旋转
if(newNode.key<node.left.key){
node=this[rotationLL](node);
}else{
node=this[rotationLR](node);
}
}
}
}
}else{
if(node.right==null){
node.right=newNode;
}else{
node.right=this[insertNode](node.right,newNode);
if(node.right!=null){
//确认是否需要平衡
if((this[heightNode](node.right)-this[heightNode](node.left))>1){
//旋转
if(newNode.key>node.right.key){
node=this[rotationRR](node);
}else{
node=this[rotationRL](node);
}
}
}
}
}
return node;
};
this[heightNode]=function(node){
if(node==null){
return -1;
}else{
return Math.max(this[heightNode](node.left),this[heightNode](node.right))+1;
}
}
//中序遍历是一种以上行顺序访问BST所有节点的遍历方式,也就是以从最小到最大的顺序访问所有节点。中序遍历的一种应用就是对树进行排序操作。
this[inOrderTraverseNode]=function(node,callback){
if(node!=null){
this[inOrderTraverseNode](node.left,callback);//左
callback(node.key);//根
this[inOrderTraverseNode](node.right,callback);//右
}
};
//先序遍历是以优先于后代节点的顺序访问每个节点的。先序遍历的一种应用是打印一个结构化的文档
this[preOrderTraverseNode]=function(node,callback){
if(node!=null){
callback(node.key);//根
this[inOrderTraverseNode](node.left,callback);//左
this[inOrderTraverseNode](node.right,callback);//右
}
}
//后序遍历则是先访问节点的后代节点,再访问节点本身。后序遍历的一种应用是计算一个目录和它的子目录中所有文件所占空间的大小。
this[postOrderTraverseNode]=function(node,callback){
if(node!=null){
this[inOrderTraverseNode](node.left,callback);//左
this[inOrderTraverseNode](node.right,callback);//右
callback(node.key);//根
}
}
//搜索最小值
this[minNode]=function(node){
if(node){
while(node&&node.left!=null){
node=node.left;
}
return node.key;
}
return null;
}
//搜索最大值
this[maxNode]=function(node){
if(node){
while(node&&node.right!=null){
node=node.right;
}
return node.key;
}
return null;
}
//搜索特定值
this[searchNode]=function(node,key){
if(node==null){
return false;
}
if(key<node.key){
return this[searchNode](node.left,key);
}else if(key>node.key){
return this[searchNode](node.right,key);
}else{
return true;
}
}
//移除一个节点
this[removeNode]=function(node,key){
if(node==null){
return null;
}
if(key<node.key){
node.left=this[removeNode](node.left,key);
return node;
}else if(key>node.key){
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;
}else if(node.right==null){
node=node.left;
return node;
}else{//同时拥有两个子节点
let aux=this[findMinNode](node.right);//找到被删除节点的右支最小的值
node.key=aux.key;//右支最小值替换被删除的值
node.right=this[removeNode](node.right,aux.key);//删除原右支最小值的节点
return node;
}
}
}
this[findMinNode]=function(node){
while(node&&node.left!=null){
node=node.left;
}
return node;
}
this[rotationRR]=function(node){//当插入的节点在右右节点
let tmp=node.right;
node.right=tmp.left;
tmp.left=node;
return tmp;
}
this[rotationLL]=function(node){//当插入的节点在左左节点
let tmp=node.left;
node.left=tmp.right;
tmp.right=node;
return tmp;
}
this[rotationLR]=function(node){//当插入的节点在左右节点
node.left=this[rotationRR](node.left);//向左单旋转
return this[rotationLL](node);//再向右单旋转
}
this[rotationRL]=function(node){//当插入的节点在右左节点
node.right=this[rotationLL](node.right);//向右单旋转
return this[rotationRR](node);//再向左单旋转
}
}
insert(key){
let newNode=new Node(key);
if(this.root==null){//树为空
this.root=newNode;
}else{//树不为空
this[insertNode](this.root,newNode);
}
}
inOrderTraverse(callback){
this[inOrderTraverseNode](this.root,callback);
}
preOrderTraverse(callback){
this[preOrderTraverseNode](this.root,callback);
}
postOrderTraverse(callback){
this[postOrderTraverseNode](this.root,callback);
}
min(){
return this[minNode](this.root);
}
max(){
return this[maxNode](this.root);
}
search(key){
return this[searchNode](this.root,key);
}
remove(key){
this.root=this[removeNode](this.root,key)
}
}
let tree=new AVLTree();
tree.insert(7);
tree.insert(15);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);
tree.insert(6);
tree.inOrderTraverse(function(value){
console.log("inOrderTraverse:",value);
})
tree.preOrderTraverse(function(value){
console.log("preOrderTraverse:",value);
})
tree.postOrderTraverse(function(value){
console.log("postOrderTraverse:",value);
})
console.log(tree.min());
console.log(tree.max());
console.log(tree.search(8))//true
tree.remove(8)
console.log(tree.search(8))//false