排序二叉树有以下缺点:
同样的关键字集合有可能导致不同的树结构索引,如下图的右图所示,此时他的搜索性能已经是线性的了,为了解决这种"树一边倒的现象",自平衡二叉树就被提出来了。平衡二叉树的目的是为了减少二叉查找树层次,提高查找速度。
平衡二叉树
定义:平衡二叉树或为空树,又被称为AVL树(有别于AVL算法)或为如下性质的二叉排序树:
(1)左右子树深度之差的绝对值不超过1;
(2)左右子树仍然为平衡二叉树.
平衡二叉树必定是二叉搜索树,反之则不一定。
平衡二叉树的常用实现方法:AA树、AVL树、红黑树、树堆Treap、伸展树等
AVL 树是高度平衡的,频繁的插入和删除,会引起频繁的reblance,导致效率下降
红黑树不是高度平衡的,算是一种折中,插入最多两次旋转,删除最多三次旋转。
平衡因子(平衡度)
平衡因子=左子树高度-右子树高度
当平衡因子为1,-1,0时,当前的树处于平衡状态,当平衡因子为2,-2时,当前树处于不平衡状态,需要进行自平衡
如何进行自平衡
- 找平衡因子=2/-2
- 找插入新结点后失去平衡的最小子树
该子树要求:
1.距离插入结点最近
2.平衡因子绝对值为1的结点作为根 - 平衡调整(有4种情况如下)
左旋和右旋?
总结了一句口诀(主要用于写代码):
左旋:其右子(若有)给其父结点的左结点
右旋:其左子(若有)给其父结点的右结点
如图演示:
代码实现
叶点类:
class AVLTreeNode{
public int data;
public AVLTreeNode left;
public AVLTreeNode right;
public int height;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public AVLTreeNode getLeft() {
return left;
}
public void setLeft(AVLTreeNode left) {
this.left = left;
}
public AVLTreeNode getRight() {
return right;
}
public void setRight(AVLTreeNode right) {
this.right = right;
}
public int getheight() {
return height;
}
public void setheight(int height) {
this.height = height;
}
public AVLTreeNode(int data){
initiateNode(data,null,null,1);
}
public AVLTreeNode(int data,AVLTreeNode left,AVLTreeNode right,int height)
{
initiateNode(data,left,right,height);
}
public void initiateNode(int date,AVLTreeNode left,AVLTreeNode right,int height)
{
this.setData(date);
this.left=left;
this.right=right;
this.height=height;
}
// 返回左子树的高度
public int leftHeight() {
if (left == null) {
return 0;
}
return left.height();
}
// 返回右子树的高度
public int rightHeight() {
if (right == null) {
return 0;
}
return right.height();
}
//递归返回以该结点为根结点的树的高度
public int height() {
//+1因为本身也占一层
return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
}
}
LL型旋转:
/*
* LL型旋转;左左对应的情况(左单旋转)
* 传入值:旋转前的根结点
* 返回值:旋转后的根节点
*/
private AVLTreeNode leftLeftRotation(AVLTreeNode root) {
AVLTreeNode rootLeft=root.left;
//右子结点给父结点的左结点
root.left=rootLeft.right;
//将原根结点作为rootLeft的右结点,让rootLeft成为新的根结点
rootLeft.right=root;
//重新计算高度
root.height = root.height();
rootLeft.height = rootLeft.height();
return rootLeft;
}
private AVLTreeNode rightRightRotation(AVLTreeNode root) {
AVLTreeNode rootRight=root.right;
//左子结点给父结点的右结点
root.right=rootRight.left;
//将原根结点作为rootRight的左结点,让rootRight成为新的根结点
rootRight.left=root;
//重计算高度
root.height = root.height();
rootRight.height=rootRight.height();
return rootRight;
}
LR旋转:需要经过两次旋转(右旋后左旋)才能让AVL树恢复平衡
private AVLTreeNode leftRightRotation(AVLTreeNode root) {
//先右旋再左旋
root.left = rightRightRotation(root.left);
return leftLeftRotation(root);
}
private AVLTreeNode rightLeftRotation(AVLTreeNode root) {
root.right = leftLeftRotation(root.right);
return rightRightRotation(root);
}