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)