/*
* 文 件 名: AVLTree.java
* 修改时间: 2012-11-30
*/
package tree;
/**
* AVL树
*
* @version [版本号, 2012-11-30]
*/
public class AVLTree
{
/**
* AVL树的根节点
*/
private AVLNode root;
/**
* <默认构造函数>
*/
public AVLTree()
{
root = null;
}
/**
* 判断树是否为空
*
* @return
* @see [类、类#方法、类#成员]
*/
public boolean isEmpty()
{
return root == null;
}
/**
* 查找指定关键字的插入位置,小于插左边,大于或者等于插右边
*
* @return
* @see [类、类#方法、类#成员]
*/
public AVLNode findInsert(int data)
{
AVLNode current = root;
while (current != null)
{
if (data < current.data)
{
if (current.left == null)
{
break;
}
else
{
current = current.left;
}
}
else
{
if (current.right == null)
{
break;
}
else
{
current = current.right;
}
}
}
return current;
}
/**
* AVL树插入操作,最重要的操作
*
* @param data
* @see [类、类#方法、类#成员]
*/
public void insert(int data)
{
// 新建一个节点
AVLNode insertNode = new AVLNode(data);
// 如果根节点为空,直接赋值给根节点
if (root == null)
{
root = insertNode;
return;
}
AVLNode insertPos = root;
// 先将节点插入树中,需要回朔改变插入路径的各个节点的平衡因子。
while (insertPos != null)
{
if (data < insertPos.data)
{
if (insertPos.left == null)
{
insertPos.left = insertNode;
insertPos.bf += 1;
insertNode.parent = insertPos;
break;
}
else
{
insertPos = insertPos.left;
}
}
else
{
if (insertPos.right == null)
{
insertPos.right = insertNode;
insertPos.bf -= 1;
insertNode.parent = insertPos;
break;
}
else
{
insertPos = insertPos.right;
}
}
}
// 下面是回朔算法,从insertPos节点开始检查
AVLNode current = insertPos;
while (true)
{
// 如果节点的平衡因子为0,不需要再回朔了,直接返回
if (current.bf == 0)
{
return;
}
// 如果节点的平衡因子为1或者-1,首先修正父节点的平衡因子,但是要先判断当前节点是否是root
else if (current.bf == 1 || current.bf == -1)
{
// 如果已经回朔到根节点,则直接返回
if (current == root)
{
return;
}
// 如果插入的节点在当前节点的父节点的左边,则将父节点的bf值+1
else if (data < current.parent.data)
{
current.parent.bf += 1;
}
// 否则当前节点的父节点的bf值减一
else
{
current.parent.bf -= 1;
}
// 继续回朔当前的父节点
current = current.parent;
}
// 如果节点的平衡因子的绝对值大于1,则说明找到了最小不平衡子树的根,下面左旋转修正操作
else if (current.bf == 2 || current.bf == -2)
{
// 比较一下data数据的大小即可知道插入的是左边还是右边了。。。
// /首先确定是什么类型的旋转RR,LL,RL,LR
// LL型,插在当前节点的左子树的左子树,因为data小于当前节点的data,
// 所以当前节点的左子树必不为空,即有两个节点位置需要调整,current、current.left
// A-current
// /
// B-current.left
// /
// C 需要将current绕着B节点右旋,bf值改变的节点为A和B
if (data < current.data && data < current.left.data)
{
// 这里需要分两种情况,一种是当前节点为根节点,一种是非根节点
// 还要再分两种情况,一种是current.left有右节点,一种是current.left没有右节点
// 这里有个疑问,如何在调整完节点位置之后修正节点的bf值
// 如果当前节点为根节点,即父节点为null
rightRotate(current);
}
// RR类型旋转
else if (data >= current.data && data >= current.right.data)
{
leftRotate(current);
}
// LR型旋转,先要将current.left绕着current.left.right左旋,再将current绕着current.left右旋
else if (data < current.data && data >= current.left.data)
{
leftRotate(current.left);
rightRotate(current);
}
else
{
rightRotate(current.right);
leftRotate(current);
}
// 如果B的bf值为0或者B为根节点,则跳出循环
if (current.parent == root || current.parent.bf == 0)
{
break;
}
// 否则继续检查B的父节点
else
{
current = current.parent.parent;
}
}
}
}
/**
* AVL树删除操作,首先找到删除的节点,如果左子树不为空,则将待删除节点的数据和左子树中data最大的节点的数据交换,否则如果右子树不为空,
* 则将待删除节点与右子树中最小的节点交换数据,实际删除的都是叶子节点,然后回朔修改bf值
*
* @return
*/
public AVLNode delete(int data)
{
AVLNode current = root;
while (current != null)
{
if (data < current.data)
{
current = current.left;
}
else if (data > current.data)
{
current = current.right;
}
else
{// 找到该节点了,下面是找到实际的叶子节点与该节点交换
// 如果该节点的左子树不为空,则将左子树的最大节点与该节点交换
int temp = current.data;
AVLNode tempNode = null;
if (current.left != null)
{
tempNode = maxNode(current.left);
}
else if (current.right != null)
{
tempNode = minNode(current.right);
}
if (tempNode != null)
{
current.data = tempNode.data;
tempNode.data = temp;
current = tempNode;
}
// 下面是具体的删除操作了,其实就是删除叶子节点了
if (current == root)
{
root = null;
return current;
}
else if (current == current.parent.left)
{// 如果当前节点是其父节点的左子树
if (current.left != null)
{// 如果当前节点的左子树不为空
current.parent.left = current.left;
current.left.parent = current.parent;
}
else if (current.right != null)
{
current.parent.left = current.right;
current.right.parent = current.parent;
}
else
{
current.parent.left = null;
}
current.parent.bf -= 1;
}
else
{// 如果当前节点是其父节点的右子树
if (current.left != null)
{// 如果当前节点的左子树不为空,则将当前节点的左子树链接到当前节点的父节点
current.parent.right = current.left;
current.left.parent = current.parent;
}
else if (current.right != null)
{
current.parent.right = current.right;
current.right.parent = current.parent;
}
else
{
current.parent.right = null;
}
current.parent.bf += 1;
}
tempNode = current.parent;// 这里用上面的tempNode节点作为回朔的开端,省的定义太多变量很乱
// 下面是删除操作的回朔算法
while (true)
{
// 如果节点的bf为1或者-1,说明删除之前的bf为0(因为删除之前是平衡的,不可能为2或者-2情况),删除之后树的高度没有改变,不需要再回朔判断
if (tempNode.bf == 1 || tempNode.bf == -1)
{
return current;
}
else if (tempNode.bf == 0)
{// 如果节点的bf为0,说明删除之前bf为1或者-1,删除之后高度减少了,那么需要首先判断当前节点是父节点的左子树还是右子树,再回朔父节点
// 首先需要判断是否已经回朔到了根节点
if (tempNode == root)
{
return current;
}
else if (tempNode == tempNode.parent.left)
{// 如果当前节点是父节点的左子树,说明父节点的左子树高度下降了
tempNode.parent.bf -= 1;
}
else
{
tempNode.parent.bf += 1;
}
}
else
{// 如果节点的bf为2或者-2,则需要通过旋转保持平衡,首先确定旋转的类型,bf值大于等于0向左,小于0向右
if (tempNode.bf >= 0 && tempNode.left.bf >= 0)
{// LL型旋转
rightRotate(tempNode);
}
else if (tempNode.bf >= 0 && tempNode.left.bf < 0)
{// LR型旋转
leftRotate(tempNode.left);
rightRotate(tempNode);
}
else if (tempNode.bf < 0 && tempNode.right.bf >= 0)
{// RL型旋转
rightRotate(tempNode.right);
leftRotate(tempNode);
}
else
{// RR型旋转
leftRotate(tempNode);
}
}
tempNode = tempNode.parent;
}
}
}
return current;
}
/**
* 返回一个树的最大值节点
*
* @param root
* @return
*/
private AVLNode maxNode(AVLNode root)
{
AVLNode current = root;
while (current != null)
{
if (current.right == null)
{
break;
}
else
{
current = current.right;
}
}
return current;
}
/**
* 返回一个树的最小值节点
*
* @param root
* @return
*/
private AVLNode minNode(AVLNode root)
{
AVLNode current = root;
while (current != null)
{
if (current.left == null)
{
break;
}
else
{
current = current.left;
}
}
return current;
}
/**
* 将节点左旋的方法
*
* @param node
* @see [类、类#方法、类#成员]
*/
private void leftRotate(AVLNode node)
{
// 旋转代码
if (node.parent == null)
{
root = node.right;
}
else if (node == node.parent.left)
{
node.parent.left = node.right;
}
else
{
node.parent.right = node.right;
}
node.right.parent = node.parent;
if (node.right.left != null)
{
node.right.left.parent = node;
}
node.parent = node.right;
node.right = node.parent.left;
node.parent.left = node;
// 调整平衡因子
if (node.parent.bf < 0)//如果父节点的平衡因子小于0
{
node.bf = node.bf - node.parent.bf + 1;
}
else
//如果父节点的平衡因子大于或等于0
{
node.bf += 1;
}
if (node.bf < 0)
{
node.parent.bf += 1;
}
else
{
node.parent.bf = node.parent.bf + node.bf + 1;
}
}
/**
* 将节点右旋的方法
*
* @param node
* @see [类、类#方法、类#成员]
*/
private void rightRotate(AVLNode node)
{
if (node.parent == null)
{
root = node.left;
}
else if (node == node.parent.left)
{
node.parent.left = node.left;
}
else
{
node.parent.right = node.left;
}
// 将B的父节点设为A的父节点
node.left.parent = node.parent;
// 如果B的右子树不为null,则将B的右子树的父节点设为A
if (node.left.right != null)
{
node.left.right.parent = node;
}
// 将A的父节点设为B
node.parent = node.left;
// 将B的右子树接到A的左边
node.left = node.parent.right;
node.parent.right = node;
// 求得当前节点旋转后的bf值,这个是经过公式推导出来的会有三种情况-1,0,1
// node.bf = node.bf - node.parent.bf - 1;
// if (node.bf == -1)
// {
// node.parent.bf -= 2;
// }
// else
// {
// node.parent.bf -= 1;
// }
if (node.parent.bf >= 0)
{
node.bf = node.bf - node.parent.bf - 1;
}
else
{
node.bf -= 1;
}
if (node.bf >= 0)
{
node.parent.bf -= 1;
}
else
{
node.parent.bf = node.parent.bf + node.bf - 1;
}
}
/**
* 中序遍历二叉树
*
* @see [类、类#方法、类#成员]
*/
public void middleDisplay()
{
middleDisplay(root);
}
/**
* 使用递归方式中序遍历二叉树
*
* @param root
* @see [类、类#方法、类#成员]
*/
private void middleDisplay(AVLNode root)
{
if (root != null)
{
middleDisplay(root.left);
root.displayNode();
middleDisplay(root.right);
}
}
}