二叉排序树
- 优点:既能够保证插入,删除节点的效率,同时也能兼顾查找的效率。
- 缺点:当二叉树的节点倾向一边时,其查询性能就大打折扣了,几乎变成了链表。
如图所示:
AVL树(平衡二叉树)就是来解决这个问题的。
AVL树
- 具有的特性 :
- 与二叉查找树具有相同的特性
- 每个节点的左子树和右子树之间的高度差不能超过1
图一:是一颗平衡二叉树,它的左右子树的高度差为1。
图二:不是一颗平衡二叉树,因为值为3的这个节点的左子树的高度为2,右子树的高度为0,左右子树的高度差超过1。
- 如何保证二叉树排序树是AVL树?
- 单旋转(左旋):该二叉排序树向右倾,称为右-右型,需要对其进行左旋才能让它变成AVL树。
- 单旋转(右旋):该二叉排序树向左倾,称为左-左型,需要对其进行右旋才能让它变成AVL树。
- 双旋转(先左再右):左-右型,先左旋转,再右旋转。
- 单旋转(左旋):该二叉排序树向右倾,称为右-右型,需要对其进行左旋才能让它变成AVL树。
-
双旋转(先右再左):右-左型,先右旋转,再左旋转。
小规律 :1、左-左型:做右旋。 2、右-右型:做左旋转。 3、左-右型:先做左旋,后做右旋。 4、右-左型:先做右旋,再做左旋。
右旋转的步骤:( 如下图所示)
1.创建一个新节点,用来保存值为7节点的value
2.让值为7节点的左子树指向5节点的右子树
3.将7节点的值赋给5节点,并让7节点的左子树指向5节点的左子树(相当于把原来的7节点删除)
4.让新7节点的右子树指向新节点
代码实现 :
package AVLTree;
/**
* AVL树要求每个节点的左子树和右子树的高度差不差过1
* @author liuyang
*
*/
public class AVLNode {
//节点的值
int value;
//左子树
AVLNode left;
//右子树
AVLNode right;
public AVLNode(int v) {
this.value = v;
}
//查询该节点的高度
public int height() {
return Math.max(left==null?0:left.height(), right==null?0:right.height())+1;
}
//查看该节点左子树的高度
public int leftHeight() {
if(left==null) {
return 0;
}
return left.height();
}
//查看该节点右子树的高度
public int rightHeight() {
if(right==null) {
return 0;
}
return right.height();
}
//添加节点
public void add(AVLNode node) {
//递归退出的条件
if(node==null) {
return;
}
//将添加的节点变成一颗二叉排序树
if(node.value>this.value) {
if(this.right!=null) {
right.add(node);
}else {
right = node;
}
} else {
if(this.left!=null) {
left.add(node);
}else {
left = node;
}
}
//检查是否是平衡二叉树
//右旋转
if( leftHeight()-rightHeight()>1 ) {
//双旋转
if(left!=null&&left.rightHeight()>left.leftHeight()) {
//先左旋转
left.leftRotate();
//再右旋转
this.rightRotate();
}else { //单旋转
this.rightRotate();
}
}//左旋转
else if(rightHeight()-leftHeight()>1) {
//双旋转
if(right!=null&&right.rightHeight()>right.leftHeight()) {
//先右旋转
right.rightRotate();
//再左旋转
this.leftRotate();
}else {//单旋转
this.leftRotate();
}
}
}
//右旋转
private void rightRotate() {
//先创建一个新节点,值等于该节点的value
AVLNode node = new AVLNode(this.value);
//让新节点的右子树指向这个节点的右子树
node.right = this.right;
//让新节点的左子树指向该节点左子树的右子树
node.left = left.right;
//将该节点的左子树的value赋给该节点
this.value = left.value;
//将该节点的左子树指向左子树的左子树
this.left = left.left;
//将该节点的右子树指向新的节点
this.right = node;
}
//左旋转
private void leftRotate() {
//先创建一个新节点,值等于this.value
AVLNode node = new AVLNode(this.value);
node.left = this.left;
node.right = right.left;
this.value = right.value;
this.right = right.right;
this.left = node;
}
}