这里主要解决下面问题
(1)什么是平衡二叉树
(2)为什么需要平衡二叉树
(3)实现平衡二叉树(添加和删除)
1、什么是平衡二叉树
(1)是二叉树
(2)每一个节点的左右子树的高度差小于等于1
比如下面的树
这个树看似不怎么平衡,但是这个就是平衡二叉树。上面的每个节点左边的数字表示节点的高度(叶子节点高度为1),右边的数字表示节点的平衡因子(左子节点高度-有子节点高度,叶子节点平衡因子为0)
2、为什么需要平衡二叉树
我们都知道,如果插入的序列已经是排好序的,那么对于二叉树的查找的效率就退化为对链表的查找(O(logn))。序列的总数比较多的时候,查找的效率将会发生很大的退化。下面我们做一效率的比较
我按顺序把1-10000的数字插入平衡二叉树和普通二叉树中,然后再从查找1-10000,看看运行的时间
测试的代码如下:
public static void main(String[] args) {
BST<Integer, Object> bst=new BST<>();
AVLTree<Integer, Object> avl=new AVLTree<>();
//BST
long startTime = System.nanoTime();
for(int i=1;i<=10000;i++)
{
bst.add(i);
}
for(int i=1;i<=10000;i++)
{
bst.get(i);
}
long endTime = System.nanoTime();
System.out.println("BST: "+(endTime-startTime)/(100000000-0.0)+"s");
//AVL
startTime = System.nanoTime();
for(int i=1;i<=10000;i++)
{
avl.add(i);
}
for(int i=1;i<=10000;i++)
{
avl.get(i);
}
endTime = System.nanoTime();
System.out.println("AVL: "+(endTime-startTime)/(100000000-0.0)+"s");
}
结果:
上面可以得知:平衡二叉树的效率比普通二叉树有了很大的提高
3、实现平衡二叉树
(1)需要考虑的问题
为了在添加和删除节点的时候知道这个节点是否是平衡的,所以我们要在树的节点添加一个数据项来记录节点的高度,然后通过其左子节点和右子节点的高度差得到平衡因子,最后根据平衡因子来判断这个节点是否是平衡的。如果不平衡,那么进行平衡
private class Node<K,V>{
//为了具有拓展性,比如AVL树可以用来实现map和set(set就把value设为null)
private K key;
private V value;
//左孩子
private Node<K,V> left;
private Node<K,V> right;
//这个节点的高度
private int height;
//构造函数
public Node(K key)
{
this(key, null);
}
public Node(K key,V value)
{
this.key=key;
this.value=value;
//初始高度为1
height=1;
}
}
这个是一个私有的内部类
(2)添加节点
AVL树的添加主要考虑下面4种情况
–LL:在这个节点的左子节点的左子节点位置插入,右旋转
– RR:在这个节点的右子节点的右子节点位置插入,左旋转
– LR:在这个节点的左子节点的右子节点位置插入,左旋转后右旋转
– RL:在这个节点的右子节点的左子节点位置插入,右旋转后左旋转
和上面的LR对称,这里就不画了
递归实现的代码
public void add(K key)
{
add(key, null);
}
public void add(K key, V value)
{
//使用递归来实现
root=add(root, key, value);
}
private Node<K, V> add(Node<K, V> node, K key, V value)
{
//如果这个节点为空,那么新建一个节点返回给它的父节点
if(node==null)
{
size++;
return new Node<K, V>(key, value);
}
//如果key小于目前节点,进入其左子树
if(key.compareTo(node.key)<0)
node.left=add(node.left, key, value);
//如果key大于目前节点,进入其右子树
else if(key.compareTo(node.key)>0)
node.right=add(node.right, key, value);
else
node.value=value;
//更新节点过度
node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
//这个节点的平衡因子
int balanceFactor=getHeight(node.left)-getHeight(node.right);
//System.out.println("balanceFactor="+balanceFactor+" "+node.key);
//如果平衡因子大于1或者小于-1,则需要平衡
//LL
if(balanceFactor>1&&getBalanceFactor(node.left)>0)
{
return rightRotate(node);
}
//RR
if(balanceFactor<-1&&getBalanceFactor(node.right)<0)
{
//System.out.println("RR"+" "+node.key+" "+getBalanceFactor(node));
return leftRotate(node);
}
//LR
if(balanceFactor>1&&getBalanceFactor(node.left)<0)
{
//先左旋转在右旋转
node.left=leftRotate(node.left);
return rightRotate(node);
}
//RL
if(balanceFactor<-1&&getBalanceFactor(node.right)>0)
{
//先右旋转在左旋转
node.right=rightRotate(node.right);
return leftRotate(node);
}
return node;
}
//右旋转
private Node<K, V> rightRotate(Node<K, V> node)
{
Node<K, V> node1=node.left;
Node<K, V> node2=node1.right;
node1.right=node;
node.left=node2;
node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
node1.height=Math.max(getHeight(node1.left), getHeight(node1.right))+1;
return node1;
}
//左旋转
private Node<K, V> leftRotate(Node<K, V> node)
{
Node<K, V> node1=node.right;
Node<K, V> node2=node1.left;
node1.left=node;
node.right=node2;
node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
node1.height=Math.max(getHeight(node1.left), getHeight(node1.right))+1;
return node1;
}
(3)删除节点,删除节点的平衡和添加节点的完全一样,当删除节点递归返回后,判断节点是否平衡,如果不平衡,那么需要先进行平衡,最后把平衡后这个子树的根节点返回
代码实现
public void remove(K key)
{
root=remove(root,key);
}
private Node<K, V> remove(Node<K, V> node,K key)
{
//如果node为空,表示找不到
if(node==null)
return null;
Node<K, V> retNode;
//key小于node的key,那么在其左子树上删除
if(key.compareTo(node.key)<0)
{
node.left=remove(node.left,key);
retNode=node;
}
//key大于node的key,那么在其右子树上删除
else if(key.compareTo(node.key)>0)
{
node.right=remove(node.right, key);
retNode=node;
}
//key等于node的key,那么删除node,然后维护这个新树
else
{
size--;
//表示删除node
//node左子树如果是null,那么返回其右子树即可
if(node.left==null)
retNode=node.right;
//node左子树不为null,右子树是null,那么返回其左子树即可
else if(node.right==null)
retNode=node.left;
//node的左右子树都不为null,那么返回node节点中序遍历的后继节点,同时维护这棵新树
else {
//node的右子节点
Node<K, V> nodeMidNext=node.right;
//如果nodeRight的左子树为空,那nodeRight就是node节点中序遍历的后继节点
if(nodeMidNext.left==null)
{
//维护新数
nodeMidNext.left=node.left;
retNode=nodeMidNext;
}
//如果其左子树不为null,那么nodeRight的最左子树就是node节点中序遍历的后继节点
else
{
//这个后继节点的父节点
Node<K, V> nodeMidNextParent=nodeMidNext;
nodeMidNext=nodeMidNext.left;
//找到这个后继节点
while(nodeMidNext.left!=null)
{
nodeMidNextParent=nodeMidNext;
nodeMidNext=nodeMidNext.left;
}
//维护新数
nodeMidNextParent.left=nodeMidNext.right;
nodeMidNext.left=node.left;
nodeMidNext.right=node.right;
retNode=nodeMidNext;
}
}
}
if(retNode!=null)
{
//维护平衡
int balanceFactor=getHeight(retNode.left)-getHeight(retNode.right);
System.out.println("balanceFactor="+balanceFactor+" "+retNode.key);
//如果平衡因子大于1或者小于-1,则需要平衡
//LL
if(balanceFactor>1&&getBalanceFactor(retNode.left)>0)
{
return rightRotate(retNode);
}
//RR
if(balanceFactor<-1&&getBalanceFactor(retNode.right)<0)
{
return leftRotate(retNode);
}
//LR
if(balanceFactor>1&&getBalanceFactor(retNode.left)<0)
{
//先左旋转在右旋转
retNode.left=leftRotate(retNode.left);
return rightRotate(retNode);
}
//RL
if(balanceFactor<-1&&getBalanceFactor(retNode.right)>0)
{
//先右旋转在左旋转
retNode.right=rightRotate(retNode.right);
return leftRotate(retNode);
}
}
return retNode;
}
public int size()
{
return this.size;
}
private int getHeight(Node<K, V> node)
{
if(node==null)
return 0;
return node.height;
}
private int getBalanceFactor(Node<K, V> node)
{
if(node==null)
return 0;
return (getHeight(node.left)-getHeight(node.right));
}