JAVA数据结构与算法(七)java实现AVL树
1,AVL树又称平衡二叉树,它首先是一颗二叉查找树,但在二叉查找树中,某个结点的左右子树高度之差的绝对值可能会超过1,称之为不平衡。而在平衡二叉树中,任何结点的左右子树高度之差的绝对值会小于等于 1。
2,为什么需要AVL树呢?在二叉查找树中最坏情况下查找某个元素的时间复杂度为O(n),而AVL树能保证查找操作的时间复杂度总为O(logn)。
对于一棵BST树而言,不仅有查找操作,也有插入、删除等改变树的形态的操作。随着不断地插入、删除,BST树有可能会退化成链表的形式,使得查找的时间复杂度变成O(N),这种情形下,BST树的结构非常不平衡了。为了保持树的平衡,需要对树的形态做一些限制,因此,引入了AVL树,以保证树的左右子树高度之差的绝对值小于等于1。
如果一棵树非空,其任意一个节点的左右子树之间的高度差的绝对值不超过1(<=1),那么其就是平衡二叉树。
左右子树的高度差的绝对值称为平衡因子
// 对节点进行向左旋转操作,返回旋转后的根节点x
// y x
// / \ / \
//T1 x y z
// / \ ----> / \ / \
// T2 z T1 T2 T3 T4
// / \
// T3 T4
// 对节点进行向右旋转操作,返回旋转后的根节点x
// y x
// / \ / \
// x T4 z y
// / \ ----------> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
AVL树的平衡保证算法
当向AVL树中插入或者删除元素时,可能打破树的平衡。这时,需要通过旋转来调整使之重新变成一颗AVL树。(这里讨论插入元素时的调整)
设 N 表示最接近新叶子的不平衡结点,由于插入元素之前树是平衡的,则插入之后不会有比 N 更高的不平衡结点。(树根的高度为 1 )
一共有 4 种原因导致结点 N 不平衡:
①在 结点 N 的左孩子的左子树中发生了插入操作(LL)
进行右旋转调整:即对结点 N 进行右旋转
②在 结点N 的右孩子的右子树中发生了插入操作(RR)
进行左旋转调整:即对结点N进行左旋转
③在 结点N 的右孩子的左子树中发生了插入操作(RL)
进行右-左旋转,即先对结点N 的孙子(孩子的孩子,“位于新插入元素的那个方向”)进行右旋转;再对结点N 的新孩子进行左旋转
④在 结点N 的左孩子的右子树中发生了插入操作(LR)
进行左-右旋转,即先对结点N的孙子进行左旋转,再对结点N的新孩子进行右旋转
public class AVLtree<K extends Comparable<K>,V> {
class TreeNode{
private K key;
private V value;
private TreeNode leftChild;
private TreeNode rightChild;
private TreeNode parent;
private int height;
public TreeNode(K key,V value){
this.key=key;
this.value=value;
leftChild=null;
rightChild=null;
height=1;
}
}
private TreeNode root;
private int size;
public AVLtree(){
}
//获得节点的高度
public int getHeight(TreeNode node){
if(node==null){
return 0;
}return node.height;
}
//获得节点的平衡因子
public int getBanlaceFactor(TreeNode node){
if(node==null){
return 0;
}
return getHeight(node.leftChild)-getHeight(node.rightChild);
}
//判断二叉树是否为搜索二叉树
public boolean isBST(){
ArrayList<K> keys=new ArrayList<>();
inOrder(root, keys);
for(int i=1;i<keys.size();i++){
if(keys.get(i-1).compareTo(keys.get(i))>0){
return false;
}
}
return false;
}
//判断是否是平衡二叉树
public boolean isBalanced(){
return isBalanced(root);
}
public boolean isBalanced(TreeNode node){
if(node==null){
return true;
}int balanceFactor=getBanlaceFactor(node);
if(Math.abs(balanceFactor)>1){
return false;
}return isBalanced(node.leftChild)&&isBalanced(node.rightChild);
}
// 对节点进行向右旋转操作,返回旋转后的根节点x
// y x
// / \ / \
// x T4 z y
// / \ ----------> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
public TreeNode rightRotate(TreeNode y){
TreeNode x=y.leftChild;
TreeNode T3=x.rightChild;
//向右旋转过程
x.rightChild=y;
y.leftChild=T3;
//更新height(只更新x和y,先更新y)
y.height=Math.max(getHeight(y.leftChild), getHeight(y.rightChild)+1);
x.height=Math.max(getHeight(x.leftChild), getHeight(x.rightChild)+1);
return x;
}
// 对节点进行向左旋转操作,返回旋转后的根节点x
// y x
// / \ / \
//T1 x y z
// / \ ----> / \ / \
// T2 z T1 T2 T3 T4
// / \
// T3 T4
public TreeNode leftRotate(TreeNode y){
TreeNode x=y.rightChild;
TreeNode T2=x.leftChild;
//向左旋转
x.leftChild=y;
y.rightChild=T2;
//更新height
y.height=Math.max(getHeight(y.leftChild), getHeight(y.rightChild)+1);
x.height=Math.max(getHeight(x.leftChild), getHeight(x.rightChild)+1);
return x;
}
//返回以node为根的二分搜索树的最小值所在的节点
public TreeNode minimum(TreeNode node){
if(node.leftChild==null){
return node;
}return minimum(node.leftChild);
}
//删除以node为根的二分搜索树的最小节点
//返回删除节点后的新的二分搜索树的根
public TreeNode removeMin(TreeNode node){
if(node.leftChild==null){
TreeNode rightNode=node.rightChild;
node.rightChild=null;
size--;
return rightNode;
}
node.leftChild=removeMin(node.leftChild);
return node;
}
//添加
public TreeNode add(TreeNode node,K key,V value){
if(node==null){
size++;
return new TreeNode(key,value);
}
if(key.compareTo(node.key)<0){
node.leftChild=add(node.leftChild,key,value);
}else if(key.compareTo(node.key)>0){
node.rightChild=add(node.rightChild,key,value);
}else{
node.value=value;
}
//更新高度
node.height=1+Math.max(getHeight(node.leftChild), getHeight(node.rightChild));
//更新平衡因子
int balanceFactor=getBanlaceFactor(node);
if(Math.abs(balanceFactor)>1){
System.out.println("不是平衡二叉树"+balanceFactor);
}
//平衡维护
//LL
if(balanceFactor>1&&getBanlaceFactor(node.leftChild)>=0){
return rightRotate(node);
}
//RR
if(balanceFactor<-1&&getBanlaceFactor(node.leftChild)<=0){
return leftRotate(node);
}
//LR
if(balanceFactor>1&&getBanlaceFactor(node.leftChild)<0){
node.leftChild=leftRotate(node.leftChild);
return rightRotate(node);
}
//RL
if(balanceFactor<-1&&getBanlaceFactor(node.rightChild)>0){
node.rightChild=rightRotate(node.rightChild);
return leftRotate(node);
}
return node;
}
//删除
public TreeNode remove(TreeNode node, K key){
if(node==null){
return null;
}
TreeNode retNode;
if(key.compareTo(node.key)<0){
node.leftChild=remove(node.leftChild, key);
retNode= node;
}
else if(key.compareTo(node.key)>0){
node.rightChild=remove(node.rightChild, key);
retNode= node;
}
else{ //key.compareTo(node.key)==0
//待删除节点左子树为空的情况
if(node.leftChild==null){
TreeNode rightNode=node.rightChild;
node.rightChild=null;
size--;
retNode= rightNode;
}
//待删除节点的右子树为空
else if(node.rightChild==null){
TreeNode leftNode=node.leftChild;
node.leftChild=null;
size--;
retNode= leftNode;
}
//待删除节点的左右子树不为空
else{
TreeNode successor=minimum(node.leftChild);
successor.rightChild=remove(node.rightChild,successor.key);
successor.leftChild=node.leftChild;
node.leftChild=node.rightChild=null;
retNode=successor;
}
}
if(retNode==null){
return null;
}
//更新高度
retNode.height=1+Math.max(getHeight(retNode.leftChild), getHeight(retNode.rightChild));
//更新平衡因子
int balanceFactor=getBanlaceFactor(retNode);
// if(Math.abs(balanceFactor)>1){
// System.out.println("不是平衡二叉树"+balanceFactor);
// }
//平衡维护
//LL
if(balanceFactor>1&&getBanlaceFactor(retNode.leftChild)>=0){
return rightRotate(retNode);
}
//RR
if(balanceFactor<-1&&getBanlaceFactor(retNode.leftChild)<=0){
return leftRotate(retNode);
}
//LR
if(balanceFactor>1&&getBanlaceFactor(retNode.leftChild)<0){
retNode.leftChild=leftRotate(retNode.leftChild);
return rightRotate(retNode);
}
//RL
if(balanceFactor<-1&&getBanlaceFactor(retNode.rightChild)>0){
retNode.rightChild=rightRotate(retNode.rightChild);
return leftRotate(retNode);
}
return retNode;
}
//中序
public void inOrder(TreeNode head,ArrayList<K> keys){
if(head==null){
return;
}
inOrder(head.leftChild,keys);
keys.add(head.key);
inOrder(head.rightChild,keys);
}