`
lt1988
  • 浏览: 17213 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

AVLTree

阅读更多

最近小看了下hsqldb的源码,发现它的索引时用AVLTree实现的,为了更深刻理解AVLTree,自己模仿hsqldb的AVLTree写了一个小平衡二叉树。目前不支持并发,好像java的map 里面有个树形结构实现。

public class AVLNode {

	public AVLNode left=null;
	public AVLNode right=null;
	public AVLNode parent=null;
	public Comparable key;
	public Object value;
	public int balance=0;
	
	public AVLNode(Comparable key,Object value)
	{
		this.key=key;
		this.value=value;
	}
	
	public boolean isFromLeft()
	{
		return this.parent.left==this;
	}
	
	public void childAs(boolean isLeft,AVLNode newParent)
	{
		this.parent=newParent;
		if(isLeft)
			newParent.left=this;
		else
			newParent.right=this;
	}
	
	public AVLNode getSmallest()
	{
		AVLNode node=this;
		AVLNode tmpNode=this;
		while(tmpNode!=null)
		{
			node=tmpNode;
			tmpNode=tmpNode.left;
		}
		return node;
	}
	
	public AVLNode getLargest()
	{
		AVLNode node=this;
		AVLNode tmpNode=this;
		while(tmpNode!=null)
		{
			node=tmpNode;
			tmpNode=tmpNode.right;
		}
		return node;
	}
}

 

import java.util.Iterator;


public class AVLTree {

	private AVLNode root=null;

	public AVLNode getRoot() {
		return root;
	}

	public void insert(AVLNode toInsert)
	{
		//whether to check this node's child
		if(toInsert.left!=null||toInsert.right!=null)
			;
		if(root==null)
			root=toInsert;
		//locate this node's position
		AVLNode tmpNode=root;
		AVLNode insertParent=null;<script type="text/javascript"><!--mce:0--></script> 
		while(tmpNode!=null)
		{
			int compare=toInsert.key.compareTo(tmpNode.key);
			if(compare==0)
				return;
			else if(compare==-1)
			{
				insertParent=tmpNode;
				tmpNode=tmpNode.left;
			}
			else 
			{
				insertParent=tmpNode;
				tmpNode=tmpNode.right;
			}
		}
		//get the left or right 
		boolean isLeft=toInsert.key.compareTo(insertParent.key)==-1?true:false;
		toInsert.childAs(isLeft, insertParent);
		AVLNode toBalance=traceBackRefreshBalanceInsert(toInsert);
		if(toBalance!=null)
			balance(toBalance);
	}
	//trace back to refresh the balances and find where need to rotate
	private AVLNode traceBackRefreshBalanceInsert(AVLNode traceNode)
	{
		int childFlag;//from left or right -1 left 1 right
		while(traceNode!=root)
		{
			childFlag=traceNode.isFromLeft()?-1:1;
			traceNode.parent.balance+=childFlag;//refresh
			switch(Math.abs(traceNode.parent.balance))
			{
			case 0://this subTree is balanced
				return null;
			case 1://continue refresh
				break;
			case 2://subTree traceNode.parent.balance is not balance!
				return traceNode.parent;
			}
			traceNode=traceNode.parent;
		}
		return null;
	}
	private AVLNode traceBackRefreshBalanceDelete(AVLNode traceNode)
	{
		AVLNode tmpNode=traceNode;
		int isFromLeft;
		while(tmpNode!=root)
		{
			isFromLeft=tmpNode.isFromLeft()?-1:1;
			tmpNode.parent.balance-=isFromLeft;
			switch(Math.abs(tmpNode.parent.balance))
			{
			case 0:
				break;
			case 1:
				return null;
			case 2:		
				return tmpNode.parent;
			}
		}
		
		return null;
	}
	
	//balance the tree
	private void balance(AVLNode toBalance)
	{
		if(toBalance.balance<0)
		{
			if(toBalance.left.balance<=0)//LL right rotate
			{
				AVLNode toBalanceLeftChild=toBalance.left;
				AVLNode toBalanceNewLeftChild=toBalance.left.right;
				toBalance.balance=0;
				toBalance.left.balance=0;
				if(toBalance.parent==null)
				{
					root=toBalanceLeftChild;
					toBalanceLeftChild.parent=null;
				}
				else
					toBalanceLeftChild.childAs(toBalance.isFromLeft(), toBalance.parent);
				toBalance.childAs(false, toBalanceLeftChild);
				if(toBalanceNewLeftChild!=null)
					toBalanceNewLeftChild.childAs(true, toBalance);
			}
			else//LR left-right rotate
			{
				AVLNode newLeftChild=toBalance.left;
				AVLNode newRoot=toBalance.left.right;
				AVLNode newLeftChildRight=toBalance.left.right.left;
				AVLNode newRightChildLeft=toBalance.left.right.right;
				newLeftChild.balance=0;
				newRoot.balance=0;
				if(toBalance.parent==null)
				{
					root=newRoot;
					newRoot.parent=null;
				}
				else
					newRoot.childAs(toBalance.isFromLeft(), toBalance.parent);
				newLeftChild.childAs(true, newRoot);
				toBalance.childAs(false, newRoot);
				if(newLeftChildRight!=null)
					newLeftChildRight.childAs(false, newLeftChild);
				if(newRightChildLeft!=null)
				newRightChildLeft.childAs(true, toBalance);
			}
		}
		else
		{
			if(toBalance.right.balance>=0)//RR left rotate
			{
				AVLNode toBalanceRightChild=toBalance.right;
				AVLNode toBalanceNewRightChild=toBalance.right.left;
				toBalance.balance=0;
				toBalance.right.balance=0;
				if(toBalance.parent==null)
				{
					toBalanceRightChild.parent=null;
					root=toBalanceRightChild;
				}
				else
					toBalanceRightChild.childAs(toBalance.isFromLeft(), toBalance.parent);
				toBalance.childAs(true, toBalanceRightChild);
				if(toBalanceNewRightChild!=null)
					toBalanceNewRightChild.childAs(false, toBalance);
			}
			else//RL right-left rotate
			{
				AVLNode newRightChild=toBalance.right;
				AVLNode newRoot=toBalance.right.left;
				AVLNode newLeftChildRight=toBalance.right.left.left;
				AVLNode newRightChildLeft=toBalance.right.left.right;
				newRightChild.balance=0;
				newRoot.balance=0;
				if(toBalance.parent==null)
				{
					root=newRoot;
					newRoot.parent=null;
				}
				else
					newRoot.childAs(toBalance.isFromLeft(), toBalance.parent);
				newRightChild.childAs(false, newRoot);
				toBalance.childAs(true, newRoot);
				if(newLeftChildRight!=null)
					newLeftChildRight.childAs(false, toBalance);
				if(newRightChildLeft!=null)
					newRightChildLeft.childAs(true, newRightChild);
			}
		}
	}
	
	public AVLNode delete(Comparable key)
	{
		AVLNode toDelete=getAVLNodeByKey(key);
		if(toDelete==null)
			return null;
		if(toDelete==root&&toDelete.left==null&&toDelete.right==null)
		{
			root=null;
			return root;
		}
		//begin replace to delete node with a node
		//this node is the deeper subTree's smallest node of the right subTree
		//or the largest node of the left subTree
		AVLNode toReplace;
		AVLNode toBalance;
		int compare=toDelete.balance;
		if(compare<=0)
		{
			toReplace=toDelete.left.getLargest();
			toBalance=traceBackRefreshBalanceDelete(toReplace);
			if(toReplace.left!=null)
				toReplace.left.childAs(false, toReplace.parent);	
		}
		else
		{
			toReplace=toDelete.right.getSmallest();
			toBalance=traceBackRefreshBalanceDelete(toReplace);
			if(toReplace.right!=null)
				toReplace.right.childAs(true, toReplace.parent);	
		}
		if(toDelete.left!=null)
			toDelete.left.childAs(true, toReplace);	
		if(toDelete.right!=null)
			toDelete.right.childAs(false, toReplace);
		toReplace.balance=toDelete.balance;
		//set new root if toDelete is the root
		if(toDelete.parent==null)
		{
			toReplace.parent=null;
			root=toReplace;
		}
		else
		{
			toReplace.childAs(toDelete.isFromLeft(), toDelete.parent);
		}
		//balance
		if(toBalance!=null)
			balance(toBalance);
		//refresh balance again
		AVLNode tmpNode=toBalance;
		while(tmpNode.parent!=null)
		{
			int isFromLeft;
			isFromLeft=tmpNode.isFromLeft()?-1:1;
			tmpNode.parent.balance-=isFromLeft;
			tmpNode=tmpNode.parent;
		}
		//clear this node
		toDelete.left=toDelete.right=toDelete.parent=null;
		return toDelete;
	}
	
	public AVLNode getAVLNodeByKey(Comparable key)
	{
		AVLNode tmpNode=root;
		while(tmpNode!=null)
		{
			switch(key.compareTo(tmpNode.key))
			{
			case 0:
				return tmpNode;
			case -1:
				tmpNode=tmpNode.left;
				break;
			case 1:
				tmpNode=tmpNode.right;
				break;
			}
		}
		return null;
	}
	

}

 

import junit.framework.TestCase;

public class TestAVLTree extends TestCase{

	AVLTree tree=new AVLTree();
	
	public void testInsertRR()
	{
		tree=new AVLTree();
		tree.insert(new AVLNode(1,1));
		assertEquals(1, tree.getRoot().value);
		tree.insert(new AVLNode(2,2));
		assertEquals(1, tree.getRoot().value);
		assertEquals(2, tree.getRoot().right.value);
		assertEquals(1, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().right.balance);
		tree.insert(new AVLNode(3,3));
		assertEquals(2, tree.getRoot().value);
		assertEquals(3, tree.getRoot().right.value);
		assertEquals(1, tree.getRoot().left.value);
		assertEquals(0, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().left.balance);
		assertEquals(0, tree.getRoot().right.balance);
	}
	
	public void testInsertLL()
	{
		tree=new AVLTree();
		tree.insert(new AVLNode(3,3));
		assertEquals(3, tree.getRoot().value);
		tree.insert(new AVLNode(2,2));
		assertEquals(3, tree.getRoot().value);
		assertEquals(2, tree.getRoot().left.value);
		assertEquals(-1, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().left.balance);
		tree.insert(new AVLNode(1,1));
		assertEquals(2, tree.getRoot().value);
		assertEquals(3, tree.getRoot().right.value);
		assertEquals(1, tree.getRoot().left.value);
		assertEquals(0, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().left.balance);
		assertEquals(0, tree.getRoot().right.balance);
	}
	
	public void testInsertRL()
	{
		tree=new AVLTree();
		tree.insert(new AVLNode(2,2));
		assertEquals(2, tree.getRoot().value);
		tree.insert(new AVLNode(3,3));
		assertEquals(2, tree.getRoot().value);
		assertEquals(3, tree.getRoot().right.value);
		assertEquals(1, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().right.balance);
		tree.insert(new AVLNode(1,1));
		assertEquals(2, tree.getRoot().value);
		assertEquals(3, tree.getRoot().right.value);
		assertEquals(1, tree.getRoot().left.value);
		assertEquals(0, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().left.balance);
		assertEquals(0, tree.getRoot().right.balance);
	}
	
	public void testInsertLR()
	{
		tree=new AVLTree();
		tree.insert(new AVLNode(2,2));
		assertEquals(2, tree.getRoot().value);
		tree.insert(new AVLNode(1,1));
		assertEquals(2, tree.getRoot().value);
		assertEquals(1, tree.getRoot().left.value);
		assertEquals(-1, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().left.balance);
		tree.insert(new AVLNode(3,3));
		assertEquals(2, tree.getRoot().value);
		assertEquals(3, tree.getRoot().right.value);
		assertEquals(1, tree.getRoot().left.value);
		assertEquals(0, tree.getRoot().balance);
		assertEquals(0, tree.getRoot().left.balance);
		assertEquals(0, tree.getRoot().right.balance);
	}
	
	public void testGetNode()
	{
		tree=newTree();
		assertEquals(6,tree.getAVLNodeByKey(6).value);
	}
	
	public void testHeadAndTail()
	{
		tree=newTree();
		assertEquals(11,tree.getRoot().getLargest().value);
		assertEquals(1,tree.getRoot().getSmallest().value);
	}
	
	public void testDeleteRL()
	{
		tree=newTree();
		assertEquals(4,tree.getRoot().key);
		assertNull(tree.getRoot().parent);
		assertEquals(5,tree.getRoot().right.getSmallest().key);
		assertEquals(1,tree.getRoot().balance);
		tree.delete(tree.getRoot().key);
		assertEquals(5,tree.getRoot().key);
		assertEquals(0,tree.getRoot().balance);
	}
	
	private AVLTree newTree()
	{
		tree=new AVLTree();
		tree.insert(new AVLNode(2,2));
		tree.insert(new AVLNode(1,1));
		tree.insert(new AVLNode(3,3));
		tree.insert(new AVLNode(4,4));
		tree.insert(new AVLNode(5,5));
		tree.insert(new AVLNode(6,6));
		tree.insert(new AVLNode(7,7));
		tree.insert(new AVLNode(11,11));
		return tree;
	}
}

 目前只做了简单的测试。。。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics