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)