您的位置:首页 > 其它

AA树 - 红黑树的变种

2015-08-11 16:20 309 查看
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)。

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

[java] view
plaincopy

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)语句只需执行一次,用于写入一百万个不重复的整数到磁盘文件中:

[java] view
plaincopy

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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: