AA树 - 红黑树的变种

AA树 - 红黑树的变种

作者:ljs 2011-06-15

AA树是Arne Andersson教授在他的论文"Balanced search trees made simple"中介绍的一个红黑树变种,设计的目的是减少RB树考虑的cases。AA树是一颗红黑树,但是规定红色结点不能作为任何结点的左孩子,也就 是说红色结点只能作为右孩子。这样本质上跟2-3树类似(虽然后者属于B树)。另外AA树为实现方便,不再使用红黑两种颜色,而是用level标记结点。 level实际上就相当于RB树中的black height,叶子结点的level等于1(反过来,level等于1的不一定是叶子结点,因为等于1的结点可能有一个红色的右孩子),红色结点使用它的 父结点的level,黑色结点比它的父结点的level小1。另外,下面两种情况是禁止出现的:

1)连续两个水平方向链(horizontal link),所谓horizontal link是指一个结点跟它的右孩子结点的level相同(左孩子结点永远比它的父结点level小1)。这个规定其实相当于RB树中不能出现两个连续的红色结点。

2)向左的水平方向链(left horizontal link),也就是说一个结点最多只能出现一次向右的水平方向链。这是因为left horizontal link相当于左孩子能为红色结点,这在AA树的定义中是不允许的。

在插入和删除操作中,可能会出现上面两个禁止发生的情况,这时候就需要通过树的旋转操作来纠正。AA树中只有两个基本操作:skew和split。前者用 于纠正出现向左的水平方向链,后者用于纠正出现连续两个水平方向链的情况。skew就是一个右旋转,split是一个左旋转,但两者不是互逆的。skew 操作之后可能引起1)的发生(当skew之前已经有一个右孩子的level跟当前结点的level相同),这时需要配合使用split操作。split操 作的特点是新的子树的根节点level增加1, 从而会在它的父结点中出现1)(当它作为父结点的左孩子)或者在它的父结点中出现2)(当它作为父结点的右孩子而且父结点跟祖父结点的level相同), 这时需要通过skew和split操作纠正这两种情况。

由于split引起的新问题发生在parent一级局部结点,而skew引起的新问题只发生在当前局部结点,所以在实现时需要先skew,再split。

在下面的插入操作中使用递归,删除操作没有使用递归。新插入的结点level等于1。

因为AA树也是平衡BST,它的时间复杂度跟RB树一样,即O(logn),但是旋转次数相对多一些(RB树插入操作最多旋转两次,而且旋转完毕即结束rebalancing;删除操作最多旋转三次,也是旋转完毕即结束rebalancing)。

实现: (性能测试代码和结果在本段代码后面)

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
 * 
 * @author ljs
 * 2011-06-15
 * 版权说明:可以复制,但须标明出处
 * 
 */
public class AATree<T extends Comparable<T>> {
	private AANode<T> nil;
	private AANode<T> root;
	
	public static class AANode<E extends Comparable<E>>{
		E key;
		AANode<E> left;
		AANode<E> right;
		int level;
		
		public AANode(E key,AANode<E> left,AANode<E> right){
			this.key = key;
			this.left = left;
			this.right = right;
			
			this.level  = 1;
		}
		public AANode(){	
			
		}
		
		public String toString(){
			return String.valueOf(key + ":" + level);
		}
		public E getKey(){
			return this.key;
		}
		public E getLeftChild(){
			return this.left.key;
		}
		public E getRightChild(){
			return this.right.key;
		}
		public AANode<E> getLeft() {
			return left;
		}
		public AANode<E> getRight() {
			return right;
		}		
	}
	public AATree(){
		//sentinel node
		nil = new AANode<T>();
		nil.left = nil;
		nil.right = nil;
		nil.level = 0;
		
		root = nil;
	}
	
	public AANode<T> getRoot(){
		return root;
	}
	
	private AANode<T> rotateLeftChild(AANode<T> t){		
		AANode<T> x = t.left;
		t.left = x.right;
		x.right = t;
		
		return x;
	}
	
	private AANode<T> rotateRightChild(AANode<T> t){
		AANode<T> x = t.right;
		t.right = x.left;
		x.left = t;
		
		return x;
	}
	
	private AANode<T> skew(AANode<T> t){
		if(t != nil && t.left.level == t.level)
			t=rotateLeftChild(t);
		return t;
	}
	
	private AANode<T> split(AANode<T> t){
		if(t != nil && t.right.right.level == t.level){
			t = rotateRightChild(t);
			t.level++;
		}
		return t;
	}
	
	
	//single insert
	public void insert(T x) throws Exception{
		root = insert(x,root);
	}
	//batch insert
	public void insert(T[] keys) throws Exception{
		for(T x:keys){
			insert(x);
		}
	}
	
	
	//x: the AANode to insert
	//t: the root of the subtree
	//return: the new root
	private AANode<T> insert(T x,AANode<T> t) throws Exception{
		if(t==nil){
			t = new AANode<T>(x,nil,nil);
		}else if(x.compareTo(t.key)<0){
			//search the left subtree
			AANode<T> left = insert(x,t.left);
			t.left = left;
		}else if(x.compareTo(t.key)>0){
			//search the right subtree
			AANode<T> right = insert(x,t.right);		
			t.right = right;
		}else{
			//duplicate key found!
			throw new Exception("Duplicate key is found!");
		}
		t = skew(t);
		t = split(t);
		
		return t;
	}
	//single delete
	public void delete(T x) throws Exception{
		delete(x,this.root);
	}
	//batch delete
	public void delete(T[] keys) throws Exception{
		for(T x:keys){
			delete(x);
		}
	}
	//x: the AANode to delete
	//t: the root of the subtree
	//return: the new root
	private void delete(T x,AANode<T> t) throws Exception{
		//delete using BST method
		AANode<T> deleteNode = null;
		
		Stack<AANode<T>> nodes = new Stack<AANode<T>>();
		nodes.push(nil); //sentinel
		while(t != nil){
			nodes.push(t);		
		
			if(x.compareTo(t.key)==0){
				deleteNode = t;
				t = t.left; //find predecessor
			}else if(x.compareTo(t.key)<0){
				//search the left subtree
				t = t.left;
			}else {
				//search the right subtree
				t = t.right;
			}			
		}
		
		if(deleteNode == null){
			throw new Exception("No key is found!");
		}
		
		//the top element in the stack is the predecessor of the deleted node or the deleted node itself
		t = nodes.pop();
		if(deleteNode != t){
			//t is the predecessor of the deleteNode
			deleteNode.key = t.key;  //normal BST operation
		}//otherwise: t is a single black leaf node (level=1)
		
		//remove the node
		if(t.right != nil){		
			//there is a red right child (which has same level with t)
			AANode<T> parent = nodes.pop();
			if(parent == nil){
				this.root = t.right;
			}else{
				if(parent.left == t)
					parent.left = t.right;
				else
					parent.right = t.right;
			}	
			//return this.root;
		}else{
			AANode<T> parent = nodes.pop();
			if(parent == nil){
				//now return an empty tree
				//return nil;
				this.root = nil;
			}else{								
				if(parent.left == t){ 
					parent.left = nil;
				}else{ 
					parent.right = nil;
				} 
				t=parent;				
				AANode<T> rootNode = null; 
								
				//rebalance
				boolean deleteFromRight = false;
				while(!nodes.isEmpty() && t != nil){														
					parent = nodes.peek(); 									
					
					boolean doSkewOrSplit = false;
					int currLevel = t.level;
					if(t.left.level < currLevel - 1){
						if(parent != nil && parent.right == t) {
							deleteFromRight = true;
						}		
						
						t.level--;
						if(t.right.level == currLevel) {
							t.right.level--;
						}		
						doSkewOrSplit = true;
					}else if(t.right.level<currLevel - 1 
							|| (deleteFromRight && currLevel-1==t.right.level)){
						if(parent != nil && parent.right == t) {
							deleteFromRight = true;
						}		
						
						t.level--;
						
						doSkewOrSplit = true;
					}
					if(doSkewOrSplit){
						t = skew(t);	
						t.right = skew(t.right);						
						t.right.right = skew(t.right.right);
						
						t = split(t);
						t.right = split(t.right);
						
						//set the parent
						if(parent != nil) {
							if(deleteFromRight)
								parent.right = t;
							else
								parent.left = t;
						}						
					}else{ //no rotation 
						rootNode = this.root;
						break;
					}
					rootNode = t;	
					t = nodes.pop();
				}			
				//return rootNode;
				this.root = rootNode;
			}
		}		
	}
	public static void testCases() throws Exception{
		 
		AATree<Integer> aaT = new AATree<Integer>();
		try{
			aaT.delete(0);  //throw exception
			System.out.println("***Exception Test WRONG");
		}catch(Exception e){
			if(e.getMessage().startsWith("No key is found!")){
				System.out.println("Exception Test OK");
			}else{
				System.out.println("***Exception Test WRONG");
			}
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		Integer[] A = new Integer[]{0,1,2,3,4,5,6};
		aaT.insert(A);
		System.out.println("After Insert:");
		AATree.print(aaT);
			
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		A = new Integer[]{6,5,4,3,2};
		aaT.insert(A);
		System.out.println("After Insert:");
		AATree.print(aaT);
			
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		aaT.insert(1);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		aaT.delete(1);  //become an empty tree
		System.out.println("After Delete 1:");
		AATree.print(aaT);
		if(aaT.isEmptyTree()){
			System.out.println("Empty Tree Test OK");
		}else{
			System.out.println("***Empty Tree Test WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		aaT.insert(1);
		aaT.insert(2);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		aaT.delete(2);  //delete right node (red)
		System.out.println("After Delete 2:");
		AATree.print(aaT);
		if(aaT.getRoot().getKey() == 1){
			System.out.println("Delete Red Leaf OK");
		}else{
			System.out.println("***Delete Red Leaf WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		aaT.insert(1);
		aaT.insert(2);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		aaT.delete(1);  //delete 1
		System.out.println("After Delete 1:");
		AATree.print(aaT);
		if(aaT.getRoot().getKey() == 2){
			System.out.println("Delete Root OK");
		}else{
			System.out.println("***Delete Root WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		A = new Integer[]{0,1,2,3,4,6,7};
		aaT.insert(A);
		aaT.insert(5);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//tree:
/*
			3,3
        /         \
     1,2           6,2
    /   \         /   \
 0,1     2,1   4,1     7,1
				 \
				  5,1
 */
		aaT.delete(5);
		System.out.println("After Delete 5:");
		AATree.print(aaT);
		AANode<Integer> node = aaT.findNode(4);
		if(aaT.isLeaf(node)){
			System.out.println("Leaf Test OK");
		}else{
			System.out.println("***Leaf Test WRONG");
		}
		
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		A = new Integer[]{0,1,2,3,4,6,7};
		aaT.insert(A);
		aaT.insert(5);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//tree:
/*
			3,3
        /         \
     1,2           6,2
    /   \         /   \
 0,1     2,1   4,1     7,1
				 \
				  5,1
 */
		aaT.delete(3);
		System.out.println("After Delete 3:");
		AATree.print(aaT);
		node = aaT.findNode(0);
		if(node.getRightChild()==1){
			System.out.println("Right Child OK");
		}else{
			System.out.println("***Right Child WRONG");
		}
		if(aaT.getRoot().getKey()==2){
			System.out.println("Root Key OK");
		}else{
			System.out.println("***Root Key WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		A = new Integer[]{0,1,2,3,4,6,7};
		aaT.insert(A);
		aaT.insert(5);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//tree:
/*
			3,3
        /         \
     1,2           6,2
    /   \         /   \
 0,1     2,1   4,1     7,1
				 \
				  5,1
 */
		aaT.delete(4);
		System.out.println("After Delete 4:");
		AATree.print(aaT);
		if(aaT.findNode(5).level==1){
			System.out.println("Leaf Level OK");
		}else{
			System.out.println("***Leaf Level WRONG");
		}
		if(aaT.findNode(6).level==2){
			System.out.println("Subtree's Root Level OK");
		}else{
			System.out.println("***Subtree's Root Level WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		A = new Integer[]{10,85,15,70,20,60,30,50,65,80,90,40,5,55,35};
		aaT.insert(A);
		System.out.println("Before Insert 45:");
		AATree.print(aaT);
		aaT.insert(45);
		System.out.println("After Insert 45:");
		AATree.print(aaT);
		if(aaT.getRoot().key==50 && aaT.getRoot().level == 4){
			System.out.println("Root(50) Level OK");
		}else{
			System.out.println("***Root Level WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		A = new Integer[]{0,1,2,3,4,5,6};
		aaT.insert(A);
		System.out.println("Before Delete:");
		AATree.print(aaT);
		/*
            3,3
        /         \
     1,2           5,2
    /   \         /   \
 0,1     2,1   4,1     6,1
*/
		Integer[] DEL = new Integer[]{0,3};
		aaT.delete(DEL);  //until now nothing special
		System.out.println("After Delete 0 and 3:");
		AATree.print(aaT);
		aaT.delete(1);
		System.out.println("After Delete 1:");
		AATree.print(aaT);
		if(aaT.getRoot().key==4 && aaT.getRoot().level == 2){
			System.out.println("Root(4) Level OK");
		}else{
			System.out.println("***Root Level WRONG");
		}
		
		System.out.println("*************************");
		//make a tree
		aaT = new AATree<Integer>();
		buildTestTree1(aaT);	
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//test the tree
		aaT.delete(1);
		System.out.println("After Delete 1:");
		AATree.print(aaT);
		if(aaT.getRoot().key==3 && aaT.getRoot().level == 2){
			System.out.println("Root(3) Level OK");
		}else{
			System.out.println("***Root Level WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		buildTestTree1(aaT);	
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//test the tree
		aaT.delete(7);
		System.out.println("After Delete 7:");
		AATree.print(aaT);
		aaT.delete(6);
		System.out.println("After Delete 6:");
		AATree.print(aaT);
		if(aaT.getRoot().key==2 && aaT.getRoot().level == 2){
			System.out.println("Root(2) Level OK");
		}else{
			System.out.println("***Root Level WRONG");
		}
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		buildTestTree2(aaT);	
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//test the tree
		aaT.delete(7);
		System.out.println("After Delete 7:");
		AATree.print(aaT);
		
		System.out.println("*************************");
		aaT = new AATree<Integer>();
		buildTestTree3(aaT);	
		System.out.println("Before Delete:");
		AATree.print(aaT);
		//test the tree
		aaT.delete(7);
		System.out.println("After Delete 7:");
		AATree.print(aaT);
	}
	
	private static void buildTestTree1(AATree<Integer> aaT){
		
		AANode<Integer> root = aaT.makeNode(2);
		root.level = 2;
		AANode<Integer> node1 = aaT.makeNode(1);
		node1.level = 1;
		AANode<Integer> node5 = aaT.makeNode(5);
		node5.level = 2;
		AATree.attachAsLeftChild(root, node1);
		AATree.attachAsRightChild(root, node5);
		
		AANode<Integer> node3 = aaT.makeNode(3);
		node3.level = 1;
		AANode<Integer> node6 = aaT.makeNode(6);
		node6.level = 1;
		AANode<Integer> node4 = aaT.makeNode(4);
		node4.level = 1;
		AANode<Integer> node7 = aaT.makeNode(7);
		node7.level = 1;
		AATree.attachAsLeftChild(node5, node3);
		AATree.attachAsRightChild(node5, node6);
		
		AATree.attachAsRightChild(node3, node4);
		AATree.attachAsRightChild(node6, node7);
		aaT.setRoot(root);
	}
	
	private static void buildTestTree2(AATree<Integer> aaT){
		
		AANode<Integer> root = aaT.makeNode(3);
		root.level = 2;
		AANode<Integer> node1 = aaT.makeNode(1);
		node1.level = 1;
		AANode<Integer> node6 = aaT.makeNode(6);
		node6.level = 2;
		AATree.attachAsLeftChild(root, node1);
		AATree.attachAsRightChild(root, node6);
		
		AANode<Integer> node2 = aaT.makeNode(2);
		node2.level = 1;
		AANode<Integer> node4 = aaT.makeNode(4);
		node4.level = 1;
		AANode<Integer> node7 = aaT.makeNode(7);
		node7.level = 1;
		AANode<Integer> node5 = aaT.makeNode(5);
		node5.level = 1;
		AATree.attachAsLeftChild(node6, node4);
		AATree.attachAsRightChild(node6, node7);
		
		AATree.attachAsRightChild(node4, node5);
		AATree.attachAsRightChild(node1, node2);
		aaT.setRoot(root);
	}
	
	private static void buildTestTree3(AATree<Integer> aaT){
	
	AANode<Integer> root = aaT.makeNode(3);
	root.level = 2;
	AANode<Integer> node1 = aaT.makeNode(1);
	node1.level = 1;
	AANode<Integer> node6 = aaT.makeNode(6);
	node6.level = 2;
	AATree.attachAsLeftChild(root, node1);
	AATree.attachAsRightChild(root, node6);
	
	AANode<Integer> node2 = aaT.makeNode(2);
	node2.level = 1;
	AANode<Integer> node7 = aaT.makeNode(7);
	node7.level = 1;
	AANode<Integer> node5 = aaT.makeNode(5);
	node5.level = 1;
	AATree.attachAsLeftChild(node6, node5);
	AATree.attachAsRightChild(node6, node7);
	
	AATree.attachAsRightChild(node1, node2);
	aaT.setRoot(root);
}
	//utility method for test purpose
	@SuppressWarnings("rawtypes")
	public static void print(AATree aaTree){
		AANode root = aaTree.getRoot();
		System.out.format("%nin-order BST:%n");	
		NODES=0;
		AATree.recursiveInOrderTraverse(root);
		
		System.out.format("%n%n%nThe tree is:");
		AATree.displayBinaryTree(root,NODES);
		
	}
	
	private static int NODES=0;
	//utility method for test purpose
	@SuppressWarnings("rawtypes")
	private static void recursiveInOrderTraverse(AANode root){
		if(root.key == null)return;
		
		recursiveInOrderTraverse(root.left);
		System.out.format(" (%d,%d)", root.key,root.level);
		NODES++;
		recursiveInOrderTraverse(root.right);
	}
	//utility method for test purpose
	//n: the nodes number of the tree
	@SuppressWarnings("rawtypes")
	private static void displayBinaryTree(AANode root,int n){		
		if(root.key == null) return;
		
		LinkedList<AANode> queue = new LinkedList<AANode>();
		
		//all AANodes in each level
		List<List<AANode>> nodesList = new ArrayList<List<AANode>>();
		
		//the positions in a displayable tree for each level's nodes
		List<List<Integer>> nextPosList = new ArrayList<List<Integer>>();
		
		queue.add(root);
		//int level=0;
		int levelNodes = 1;	 	
		
		int nextLevelNodes = 0;		
		List<AANode> levelNodesList = new ArrayList<AANode>();	
		List<Integer> nextLevelNodesPosList = new ArrayList<Integer>();
		
		int pos = 0;  //the position of the current node
		List<Integer> levelNodesPosList = new ArrayList<Integer>();
		levelNodesPosList.add(0); //root position
		nextPosList.add(levelNodesPosList);
		int levelNodesTotal = 1;
		while(!queue.isEmpty()) {
			AANode node = queue.remove();
			
			if(levelNodes==0){		 
				nodesList.add(levelNodesList);
				nextPosList.add(nextLevelNodesPosList);
				levelNodesPosList = nextLevelNodesPosList;
				
				levelNodesList = new ArrayList<AANode>();		
				nextLevelNodesPosList = new ArrayList<Integer>();
				
				//level++; 
				levelNodes = nextLevelNodes;
				levelNodesTotal = nextLevelNodes;
				
				nextLevelNodes = 0;				
			}		
			levelNodesList.add(node);			
			
			pos = levelNodesPosList.get(levelNodesTotal - levelNodes);			
			if(node.left.key != null){
				queue.add(node.left);
				nextLevelNodes++;		
				nextLevelNodesPosList.add(2*pos);
			}
					
			if(node.right.key != null) {
				queue.add(node.right);
				nextLevelNodes++;		
				
				nextLevelNodesPosList.add(2*pos+1);
			}
						
			levelNodes--;			
		}
		//save the last level's nodes list
		nodesList.add(levelNodesList);
		
		int maxLevel = nodesList.size()-1;  //==level
		 
		//use both nodesList and nextPosList to set the positions for each node
		
		//Note: expected max columns: 2^(level+1) - 1
		int cols = 1;
		for(int i=0;i<=maxLevel;i++){
			cols <<= 1;			
		}
		cols--;
		AANode[][] tree = new AANode[maxLevel+1][cols];
		
		//load the tree into an array for later display 
		for(int currLevel=0;currLevel<=maxLevel;currLevel++){
			levelNodesList = nodesList.get(currLevel);
			levelNodesPosList = nextPosList.get(currLevel);
			//Note: the column for this level's j-th element: 2^(maxLevel-level)*(2*j+1) - 1
			int tmp = maxLevel-currLevel;
			int coeff = 1;
			for(int i=0;i<tmp;i++){
				coeff <<= 1;			
			}
			for(int k=0;k<levelNodesList.size();k++){
				int j = levelNodesPosList.get(k);
				int col = coeff*(2*j + 1) - 1;				
				tree[currLevel][col] = levelNodesList.get(k);
			}
		}
		
		//display the binary search tree
		System.out.format("%n");
		for(int i=0;i<=maxLevel;i++){
			for(int j=0;j<cols;j++){
				AANode node = tree[i][j];
				if(node== null || node.key == null)
					System.out.format("  ");
				else
					System.out.format("%2d,%d",node.key,node.level);
			}
			System.out.format("%n");
		}
	}
	
	public void setRoot(AANode<T> root) {
		this.root = root;
	}
	public boolean isEmptyTree(){
		return this.root == nil;
	}
	
	public AANode<T> makeNode(T key){
		return new AANode<T>(key,nil,nil);
	}
	
	public static <T extends Comparable<T>> void attachAsLeftChild(AANode<T> parent,AANode<T> node){
		parent.left = node;
	}
	public static <T extends Comparable<T>> void attachAsRightChild(AANode<T> parent,AANode<T> node){
		parent.right = node;
	}
	
	public boolean isLeaf(AANode<T> node){
		return node.getLeft()==nil && node.getRight() == nil;
	}
	
	public AANode<T> findNode(T key) throws Exception{
		AANode<T> node = this.root;
		while(node != nil){
			if(key.compareTo(node.key)==0){
				break;
			}else if(key.compareTo(node.key)<0){
				node = node.left;
			}else if(key.compareTo(node.key)>0){
				node = node.right;
			}
		}
		if(node == nil){
			throw new Exception("No such key is found!");
		}
		return node;
	}
	
	public static void main(String[] args) throws Exception {
		testCases();
	}
}

性能测试:从磁盘读入一百万条数据到内存,然后依次查找、插入和删除各50万次。

先是性能测试代码:

其中prepareTestData(dataFilename,N)语句只需执行一次,用于写入一百万个不重复的整数到磁盘文件中:

	public static void main(String[] args) throws Exception {	
		String dataFilename = "C:\\tmp\\aatree.txt";
		int N = 1000000;  //the number of keys 
		
		//******Prepare Data(only execute once for multiple tests)******
		prepareTestData(dataFilename,N);
		
		//******Begin Test******
		AATree<Integer> aaT = new AATree<Integer>();
		DataInputStream in= new DataInputStream(new FileInputStream(dataFilename));
		//long startTime = System.currentTimeMillis();		
		int key = -1;
		int keyCount = 0;
		while(true){
			try{
				key = in.readInt();
				aaT.insert(key);
				keyCount++;
			}catch(EOFException e){
				break;
			}
		}		
		in.close();
		//long endTime = System.currentTimeMillis();
		//System.out.format("file io and insert time spent: %d(ms)%n",(endTime-startTime));
		System.out.format("Total number of keys to begin with: %d%n",keyCount);
		
		long t1=0,t2=0;
		
		//do some operations in memory
		long startTime = System.currentTimeMillis();	
		t1 = startTime;
		int totalSuccess = 0;
		int totalFailed = 0;	
		
		System.out.format("%n***Search Operations***%n");
		//try to find a number of keys
		int rangeStart = 1100000;
		int rangeEnd = 1600000;
		for(int i=rangeStart;i<rangeEnd;i++){
			try{			
				AANode<Integer> node = aaT.findNode(i);
				//System.out.println("found key: " + node.key);
				totalSuccess++;
			}catch(Exception e){
				totalFailed++;
				//not found
				//System.out.println(e.getMessage());			
			}
		}
		t2 = System.currentTimeMillis();
		System.out.format("Time spent: %d(ms)%n",(t2-t1));
		System.out.format("The number of keys we try to find: %d%n",rangeEnd-rangeStart);
		System.out.format("The number of keys found: %d%n",totalSuccess);
		System.out.format("The number of keys not found: %d%n",totalFailed);
		
		//insert a number of new keys
		System.out.format("%n***Insertion Operations***%n");
		t1 = System.currentTimeMillis();	
		totalSuccess = 0;
		totalFailed = 0;
		rangeStart = 1400000;
		rangeEnd = 1900000;
		for(int i=rangeStart;i<rangeEnd;i++){
			try{			
				aaT.insert(i);
				//System.out.println("inserted key: " + i);
				totalSuccess++;
			}catch(Exception e){
				totalFailed++;
				//duplicate insert
				//System.out.println(e.getMessage());			
			}
		}		
		t2 = System.currentTimeMillis();	
		System.out.format("Time spent: %d(ms)%n",(t2-t1));
		System.out.format("The number of keys we try to insert: %d%n",rangeEnd - rangeStart);
		System.out.format("The number of keys inserted: %d%n",totalSuccess);
		System.out.format("The number of keys with insertion failed due to duplicate: %d%n",totalFailed);
		
		//delete a number of new keys
		System.out.format("%n***Deletion Operations***%n");
		t1 = System.currentTimeMillis();	
		totalSuccess = 0;
		totalFailed = 0;
		rangeStart = 400000;
		rangeEnd = 900000;
		for(int i=rangeStart;i<rangeEnd;i++){
			try{			
				aaT.delete(i);
				//System.out.println("deleted key: " + i);
				totalSuccess++;
			}catch(Exception e){
				totalFailed++;
				//not found
				//System.out.println(e.getMessage());			
			}
		}
		t2 = System.currentTimeMillis();
		System.out.format("Time spent: %d(ms)%n",(t2-t1));
		System.out.format("The number of keys we try to delete: %d%n",rangeEnd-rangeStart);
		System.out.format("The number of keys deleted: %d%n",totalSuccess);
		System.out.format("The number of keys with deletion failed due to non-existence: %d%n",totalFailed);
		
		
		long endTime = t2;
		System.out.format("%nThe above operations(search,insert,delete) total spent: %d(ms)%n",(endTime-startTime));
		
	}
	
	//write non-duplicate keys into external file for later test
	private static void prepareTestData(String dataFilename,int totalKeys) throws IOException{
		AATree<Integer> aaT = new AATree<Integer>();
		
		//multiply by a larger factor(here it is 10) to eliminate the duplicate insert
		int KEYRANGE = totalKeys*10;
		int i=0;
		while(i<totalKeys){
			int rand = (int)(Math.random()*KEYRANGE);
			try{
				aaT.insert(rand);
				i++;
				//System.out.format("insert: %d%n",rand);
			}catch(Exception e){
				//for duplicate insert: retry
				//System.out.println(e.getMessage());
			}
		} 	
		DataOutputStream out= new DataOutputStream(new FileOutputStream(dataFilename));
		persistKeys(aaT.getRoot(),out);
		out.close();
	}
	
	private static void persistKeys(AANode<Integer> root,DataOutputStream out) throws IOException{
		if(root.key == null)return;
		
		persistKeys(root.left,out);
		out.writeInt(root.key);
		persistKeys(root.right,out);
	}

性能测试报告(Intel Core2 T5500 1.66GHz, 2GB RAM):

Total number of keys to begin with: 1000000

***Search Operations***
Time spent: 2172(ms)
The number of keys we try to find: 500000
The number of keys found: 49955
The number of keys not found: 450045

***Insertion Operations***
Time spent: 1984(ms)
The number of keys we try to insert: 500000
The number of keys inserted: 450090
The number of keys with insertion failed due to duplicate: 49910

***Deletion Operations***
Time spent: 3172(ms)
The number of keys we try to delete: 500000
The number of keys deleted: 7
The number of keys with deletion failed due to non-existence: 499993

The above operations(search,insert,delete) total spent: 7328(ms)

posted @ 2011-06-16 00:08  ljsspace  阅读(1474)  评论(1编辑  收藏  举报