平衡二叉树的操作难点在于如何调整平衡,根据情况可以分为LL、RR、LR、RL旋转四种方法,这是java的递归版本,后面打算用非递归实现一下,此博客是根据博客:http://blog.csdn.net/javazejian整理而成,原博客图文并茂,应该是花了不少心思研究,讲得也非常详细,特此整理收藏。
package avltree;
/**
* 平衡二叉搜索树(AVL树)节点
*/
class AVLNode{
public AVLNode left;//左结点
public AVLNode right;//右结点
public int data;
public int height;//当前结点的高度
public AVLNode(int data) {
this(null,null,data);
}
public AVLNode(AVLNode left, AVLNode right, int data) {
this(left,right,data,0);
}
public AVLNode(AVLNode left, AVLNode right, int data, int height) {
this.left=left;
this.right=right;
this.data=data;
this.height = height;
}
}
//平衡二叉树
public class MyAVLTree {
private AVLNode root; //根节点
//或取节点的高度
private int height(AVLNode subtree){
if (subtree==null){
return 0;
}else {
int l=height(subtree.left);
int r=height(subtree.right);
return (l>r) ? (l+1):(r+1);//返回并加上当前层
}
}
/**
* 左左单旋转(LL旋转) w变为x的根结点, x变为w的右子树
* @return
*/
private AVLNode singleRotateLeft(AVLNode x){
//把w结点旋转为根结点
AVLNode w= x.left;
//同时w的右子树变为x的左子树
x.left=w.right;
//x变为w的右子树
w.right=x;
//重新计算x/w的高度
x.height=Math.max(height(x.left),height(x.right))+1;
w.height=Math.max(height(w.left),x.height)+1;
return w;//返回新的根结点
}
/**
* 右右单旋转(RR旋转) x变为w的根结点, w变为x的左子树
* @return
*/
private AVLNode singleRotateRight(AVLNode w){
//把w节点的右孩子变成新的根节点
AVLNode x=w.right;
//
w.right=x.left;
x.left=w;
//重新计算x/w的高度
w.height=Math.max(height(w.left),height(w.right))+1;
x.height=Math.max(height(x.left),w.height)+1;
//返回新的根结点
return x;
}
/**
* 左右旋转(LR旋转) x(根) w y 结点 把y变成根结点
* @return
*/
private AVLNode doubleRotateWithLeft(AVLNode x){
//w先进行RR旋转
x.left=singleRotateRight(x.left);
//再进行x的LL旋转
return singleRotateLeft(x);
}
/**
* 右左旋转(RL旋转)
* @param w
* @return
*/
private AVLNode doubleRotateWithRight(AVLNode x){
//先进行LL旋转
x.right=singleRotateLeft(x.right);
//再进行RR旋转
return singleRotateRight(x);
}
/**
* 插入方法
* @param data
*/
public void insert(int data) {
this.root=insert(data,root);
}
private AVLNode insert(int data , AVLNode p){
//说明已没有孩子结点,可以创建新结点插入了.
if(p==null){
p=new AVLNode(data);
}else if(data<p.data){//向左子树寻找插入位置
p.left=insert(data,p.left);
//插入后计算子树的高度,等于2则需要重新恢复平衡,由于是左边插入,左子树的高度肯定大于等于右子树的高度
if(height(p.left)-height(p.right)==2){
//判断data是插入点的左孩子还是右孩子
if(data<p.left.data){
//进行LL旋转
p=singleRotateLeft(p);
}else {
//进行左右旋转
p=doubleRotateWithLeft(p);
}
}
}else if (data>p.data){//向右子树寻找插入位置
p.right=insert(data,p.right);
if(height(p.right)-height(p.left)==2){
if (data<p.right.data){
//进行右左旋转
p=doubleRotateWithRight(p);
}else {
p=singleRotateRight(p);
}
}
}
else
;//if exist do nothing
//重新计算各个结点的高度
p.height = Math.max( height( p.left ), height( p.right ) ) + 1;
return p;//返回根结点
}
/**
* 删除方法
* @param data
*/
public void remove(int data) {
this.root=remove(data,root);
}
/**
* 删除操作
* @param data
* @param p
* @return
*/
private AVLNode remove(int data,AVLNode p){
if(p ==null)
return null;
//从左子树查找需要删除的元素
if(data<p.data){
p.left=remove(data,p.left);
//检测是否平衡
if(height(p.right)-height(p.left)==2){
AVLNode currentNode=p.right;
//判断需要那种旋转
if(height(currentNode.left)>height(currentNode.right)){
//LL
p=singleRotateLeft(p);
}else{
//LR
p=doubleRotateWithLeft(p);
}
}
}
//从右子树查找需要删除的元素
else if(data>p.data){
p.right=remove(data,p.right);
//检测是否平衡
if(height(p.left)-height(p.right)==2){
AVLNode currentNode=p.left;
//判断需要那种旋转
if(height(currentNode.right)>height(currentNode.left)){
//RR
p=singleRotateRight(p);
}else{
//RL
p=doubleRotateWithRight(p);
}
}
}
//已找到需要删除的元素,并且要删除的结点拥有两个子节点
else if(p.right!=null&&p.left!=null){
//寻找替换结点
p.data=findMin(p.right).data;
//移除用于替换的结点
p.right = remove( p.data, p.right );
}
else {
//只有一个孩子结点或者只是叶子结点的情况
p=(p.left!=null)? p.left:p.right;
}
//更新高度值
if(p!=null)
p.height = Math.max( height( p.left ), height( p.right ) ) + 1;
return p;
}
/**
* 查找最小值结点
* @param p
* @return
*/
private AVLNode findMin(AVLNode p){
if (p==null)//结束条件
return null;
else if (p.left==null)//如果没有左结点,那么t就是最小的
return p;
return findMin(p.left);
}
/**
* 查找最大值结点
* @param p
* @return
*/
// private AVLNode findMax(AVLNode p){
// if (p==null)//结束条件
// return null;
// else if (p.right==null)
// return p;
// return findMax(p.right);
// }
//先序遍历
public void preOrder(AVLNode Root) {
//如果不是空树
if(Root!=null) {
//先访问根节点
System.out.print(Root.data+" ");
//递归先序遍历左子树
preOrder(Root.left);
//递归先序遍历右子树
preOrder(Root.right);
}
}
//中序遍历
public void midOrder(AVLNode Root) {
//如果不是空树
if(Root!=null) {
//递归中序遍历左子树
midOrder(Root.left);
//再访问根节点
System.out.print(Root.data+" ");
//递归中序遍历右子树
midOrder(Root.right);
}
}
public static void main(String[] args) {
MyAVLTree mat=new MyAVLTree();
//插入完成后是平衡的
int[] a= {10,8,15,4,9,11,20,3,5};
for(int i=0;i<a.length;i++) {
mat.insert(a[i]);
}
System.out.print("先序遍历:");
mat.preOrder(mat.root);
System.out.println();
System.out.print("中序遍历:");
mat.midOrder(mat.root);
System.out.println();
//这里只拿出一个测试,其他也可以测试一下
//在节点8的左子树的左孩子上插入一个2,则会破坏平衡
mat.insert(2);
System.out.println("插入节点2后:LL旋转");
System.out.print("先序遍历:");
mat.preOrder(mat.root);
System.out.println();
System.out.print("中序遍历:");
mat.midOrder(mat.root);
System.out.println();
}
}