为了提高二插排序树的性能,规定树中的每个节点的左子树和右子树高度差的绝对值不能大于1。为了满足上面的要求需要在插入完成后对树进行调整。下面介绍各个调整方式。
右单旋转
如下图所示,节点A的平衡因子(左子树高度减右子树高度)为1。由于在节点A的左孩子B的左子树上插入了新节点,导致B的左子树高度增加1,从而导致A的平衡因子为2,这时为了保持平衡需要对树进行调整。
旋转的方法就是将A的变为B的右子树,将B的右子树变为A的左子树。
示例代码:
Node A= node;
Node B= node.LChild;
// 旋转
Node tmp = B.RChild;
B.RChild= A;
A.LChild= tmp;
// 更新树的高度
A.height = Math.max(height(A.LChild), height(A.RChild))+1 ;
B.height= Math.max(height(B.LChild), height(B.RChild))+1 ;
return B;
}
(每个节点我们维护了一个height的属性来记录树的高度,每次旋转完成后需要更新树的高度。因为旋转会导致书的根节点发生变化,所以每次旋转完成后需要将新的根节点返回)
左单旋转
左单旋转整好与右单旋转相反。右单旋转是因为左子树太高,而左单旋转则是因为右子树太高,需要降低其高度。
如图所示节点B的右子树高度增加1,导致节点A的平衡因子变为-2,所以需要进行左旋调整位置。
旋转方法:将A变为B的左子树,将B的左子树变为A的右子树
示例代码:
Node A= node;
Node B= node.RChild;
// 旋转
Node tmp = B.LChild;
B.LChild= A;
A.RChild= tmp;
// 更新树的高度
A.height = Math.max(height(A.LChild), height(A.RChild))+1 ;
B.height= Math.max(height(B.LChild), height(B.RChild))+1 ;
return B;
}
(每个节点我们维护了一个height的属性来记录树的高度,每次旋转完成后需要更新树的高度。因为旋转会导致书的根节点发生变化,所以每次旋转完成后需要将新的根节点返回)
先左后右旋转
上面的两种情况处理比较简单,因为插入的节点要么是根节点左孩子的左子树或者是根节点右孩子的右子树。如果插入的节点在根节点左孩子的右子树上,则需要先进行左旋然后进行右旋操作。
如图所示,插入的节点在B节点右子树上,这时需要对B节点进行左旋操作,然后对A节点进行右旋操作。
示例代码:
// 先进行左旋
LRotate(node.LChild);
// 在进行右旋
return RRotate(node);
}
代码中node节点就是图中的A节点,先对A节点的左孩子B进行左旋操作,然后对A(node)节点进行右旋操作
先右旋后左旋
当插入的节点在根节点的右孩子的左子树上,则需要进行先右旋后左旋操作。
示例代码:
private Node RLRotate(Node node){
// 再进行右旋转
RRotate(node.RChild);
// 再进行右旋
return LRotate(node);
}
插入操作
插入操作通过递归方式实现,在插入操作完成后需要对访问路径上的每个节点进行判断来确定是否要旋转。
// 先将节点插入到树中
if(node == null )
return new Node(i, 1 , node);
// 插入的值与当前节点值进行比较来确定插入的位置
if(i < node.val){
node.LChild= insert(node.LChild, i); // 判断是否进行调整
if(height(node.LChild) - height(node.RChild) == 2 ){
if(i < node.LChild.val)
// 插入的节点在左孩子的左子树上,则需要进行右旋
node = RRotate(node);
else
// 插入的节点在左孩子的右子树上,则需要先进行左旋后进行右旋
node = LRRotate(node);
}
}
else {
node.RChild= insert(node.RChild, i);
if(height(node.LChild) - height(node.RChild) == -2 ){
if(i > node.RChild.val)
node= LRotate(node);
else
node= RLRotate(node);
}
}
node.height= Math.max(height(node.LChild), height(node.RChild))+1 ;
return node;
}
//计算树的高度,主要解决空树高度的问题(空树的高度为0) private int height(Node node){ return node == null ? 0:node.height; }
判断一棵树是否是AVL树
判断时通过后续遍历的方式来比较左右子树的高度差
if(node == null ){
d.height=0 ;
return true ;
}
Depth right= new Depth();
Depth left= new Depth();
if(isBalance(node.LChild,left)&& isBalance(node.RChild, right)){
if(Math.abs(left.height - right.height)<2){ // 绝对值小于等于1
// 如果是平衡树,才有必要算深度,然后看上级是不是平衡树
d.height=(left.height>right.height?left.height:right.height)+1 ;
System.out.println("left="+left.height+" right="+right.height+" height"+d.height+" value="+ node.val);
return true ;
}
}
System.out.println("left="+left.height+" right="+right.height+" height"+d.height+" value="+ node.val);
return false ;
}
static class Depth{
int height;
}
完整代码
package com.dy.xidian; public class AVL { private Node root; static class Node{ int val; //存储数据 int height; //权重 Node LChild; //右孩子 Node RChild; //左孩子 public Node(int k, int _height){ this.val = k; this. height = _height; } } private void initAVL(int[] arr){ for(int i : arr) root = insert(root, i); } public AVL(int[] arr){ initAVL(arr); } //右旋 private Node RRotate(Node node){ Node A = node; Node B = node.LChild; //旋转 Node tmp = B.RChild; B.RChild = A; A.LChild = tmp; //更新树的高度 A.height = Math.max(height(A.LChild), height(A.RChild))+1; B.height = Math.max(height(B.LChild), height(B.RChild))+1; return B; } //左旋 private Node LRotate(Node node){ Node A = node; Node B = node.RChild; //旋转 Node tmp = B.LChild; B.LChild = A; A.RChild = tmp; //更新树的高度 A.height = Math.max(height(A.LChild), height(A.RChild))+1; B.height = Math.max(height(B.LChild), height(B.RChild))+1; return B; } //先左后右旋转 private Node LRRotate(Node node){ //先进行左旋 LRotate(node.LChild); //在进行右旋 return RRotate(node); } //先右后左旋转 private Node RLRotate(Node node){ //再进行右旋转 RRotate(node.RChild); //再进行右旋 return LRotate(node); } //计算树的高度,主要解决空树高度的问题(空树的高度为0) private int height(Node node){ return node == null ? 0:node.height; } public Node insert(Node node, int i){ //先将节点插入到树中 if(node == null) return new Node(i, 1); //插入的值与当前节点值进行比较来确定插入的位置 if(i < node.val){ node.LChild = insert(node.LChild, i); //判断是否进行调整 if(height(node.LChild) - height(node.RChild) == 2){ if(i < node.LChild.val) //插入的节点在左孩子的左子树上,则需要进行右旋 node = RRotate(node); else //插入的节点在左孩子的右子树上,则需要先进行左旋后进行右旋 node = LRRotate(node); } } else{ node.RChild = insert(node.RChild, i); if(height(node.LChild) - height(node.RChild) == -2){ if(i > node.RChild.val) node = LRotate(node); else node = RLRotate(node); } } node.height = Math.max(height(node.LChild), height(node.RChild))+1; return node; } public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7,8,9,10,11,12,13,14}; AVL avl = new AVL(arr); } }