/** * @author Peter A. Friend * @version 1.0 */ public abstract class MCBSTree { /** * The initial capacity allocated for the tree. */ protected int initial_capacity; /** * The amount the capacity of the tree is incremented when needed. */ protected int capacity_increment; /** * The total size of the tree if all nodes on all levels are occupied. */ protected int full_size; /** * The current level of the tree, numbered from 0. */ protected int current_level; /** * An array is used (sequential representation) for the binary tree. */ protected Object tree_array[]; /** * The number of items currently in the tree. */ protected int size; protected int current_capacity; /** * Constructor. * * @param initialCapacity The storage allocated for the tree upon creation. * @param capacityIncrement The additional storage allocated for the tree when needed. */ public MCBSTree(int initialCapacity, int capacityIncrement) { initial_capacity = initialCapacity; capacity_increment = capacityIncrement; current_capacity = initialCapacity; full_size = 1; size = 0; current_level = 0; tree_array = new Object[initial_capacity]; } /** * Returns an inorder sorted array of Objects from the tree. * */ public synchronized final Object[] inorder() { int stack[] = new int[current_level + 1]; int stack_index = -1; int retarray_index = 0; int tree_index = 1; if (size == 0) return null; Object retarray[] = new Object[size]; while (true) { if (tree_array[tree_index] == null) { if (stack_index == -1) return retarray; else { tree_index = stack[stack_index]; stack_index--; retarray[retarray_index] = tree_array[tree_index]; retarray_index++; tree_index = (tree_index << 1) + 1; } } else { stack_index++; stack[stack_index] = tree_index; tree_index <<= 1; } } } /** * Searches for an item in the tree. * * @param item The Object to search for. * @return The Object if it is found in the tree, null otherwise. */ public final Object search(Object item) { int compare_result; int tree_index = 1; while (tree_array[tree_index] != null) { compare_result = compare(item, tree_array[tree_index]); switch (compare_result) { case -1: /* less than */ tree_index = tree_index << 1; break; case 0: /* equal */ return tree_array[tree_index]; case 1: /* greater than */ tree_index = (tree_index << 1) + 1; break; } } return null; } /** * Inserts an item into the tree. * * @param newItem The item to insert. * @return The object inserted if not already in the tree, null otherwise. */ public final Object insert(Object newItem) { int i; int j; int desired_size; int position_level; int compare_result; int new_position; int new_position_parent; int current_level_begin_index; int current_level_end_index; int desired_position = 0; int current_position; int next_inorder_position = 0; Object temp[]; Object current_object; /* * First we need to make sure that there are enough free nodes in tree * for the current level plus one. */ verifyCapacity(); /* * If we have been given a null object we return null to indicate the * error. */ if (newItem == null) return null; /* * First we traverse the tree to find the new position for the new * object. */ i = 1; new_position = 1; while (i <= full_size) { if (tree_array[i] == null) break; current_object = tree_array[i]; compare_result = compare(newItem, current_object); switch (compare_result) { case -1: /* less than */ i = i << 1; break; case 0: /* equal */ return tree_array[i]; case 1: /* greater than */ i = (i << 1) + 1; break; } } new_position = i; /* * Now we need to check if the new position increases the height of the * tree. We do this by determining the level of the new position, and * comparing it to the current level. If they are equal, we can insert * the new item. */ position_level = levelForIndex(new_position); if (position_level == current_level) { tree_array[new_position] = newItem; size++; return newItem; } /* * If we get here then the new position increases the height of the tree. * We first check to see if the tree is full. If so, we can just * move to the next level, otherwise, we have to make adjustments. */ if (full_size == size) { current_level++; full_size = 1 + (full_size << 1); tree_array[new_position] = newItem; size++; /* * Since the level of the tree is being increased, we must check to * make sure that we have enough nodes allocated for the current level * plus one. */ verifyCapacity(); return newItem; } /* * Now we are forced to adjust the tree. We first calculate * desired_position by looking for the empty slot closest to the parent * of new_position. To aid in this, we have to calculate * current_level_begin_index, current_level_end_index and * new_position_parent. */ current_level_begin_index = beginIndexForLevel(current_level); current_level_end_index = endIndexForLevel(current_level); new_position_parent = new_position >>> 1; i = new_position_parent - 1; j = new_position_parent + 1; while (true) { if (j <= current_level_end_index) { if (tree_array[j] == null) { desired_position = j; break; } else { j++; continue; } } if (i >= current_level_begin_index) { if (tree_array[i] == null) { desired_position = i; break; } else { i--; continue; } } } tree_array[new_position] = newItem; inorderShift(new_position, desired_position); size++; return newItem; } public synchronized Object delete(Object item) { int compare_result; int tree_index = 1; Object object_to_delete; /* First, find the node that we want to delete. */ while (tree_array[tree_index] != null) { compare_result = compare(item, tree_array[tree_index]); switch (compare_result) { case -1: /* less than */ tree_index = tree_index << 1; break; case 0: /* equal */ object_to_delete = tree_array[tree_index]; /* Now we call another internal function to remove the node. */ if (this.delete(tree_index) == false) return null; else return object_to_delete; case 1: /* greater than */ tree_index = (tree_index << 1) + 1; break; } } return null; } private boolean delete(int item_index) { int previous_level_full_size; int left_index; int right_index; int min_index; int min_index_right_child; int shifted_index; int level_begin_index; /* If the node at the position is null, we just return. */ if (item_index > full_size) return false; if (tree_array[item_index] == null) return false; /* * Calculate the left and right child indices for the item. */ left_index = item_index << 1; right_index = (item_index << 1) + 1; /* * Deletion is much the same as for a standard binary tree, except that * we have to fill any holes left when we delete nodes that are NOT on * the current level. */ if ( (tree_array[left_index] == null) && (tree_array[right_index] == null) ) { /* Case where node is on the current level. */ if (levelForIndex(item_index) == current_level) { tree_array[item_index] = null; shifted_index = 0; } else { /* Case where node is not on the current level. */ tree_array[item_index] = null; level_begin_index = beginIndexForLevel(current_level); while (tree_array[level_begin_index] == null) level_begin_index++; /* Now shift the nodes. */ inorderShift(level_begin_index, item_index); shifted_index = 0; } } else { if ( (tree_array[left_index] == null) && (tree_array[right_index] != null) ) { shifted_index = 0; tree_array[item_index] = tree_array[right_index]; tree_array[right_index] = null; } else if ( (tree_array[left_index] != null) && (tree_array[right_index] == null) ) { shifted_index = 0; tree_array[item_index] = tree_array[left_index]; tree_array[left_index] = null; } else { /* * Find the smallest value in the right subtree. Replace the * deleted item with that item, then replace the minimum item with * the right child of the of the minimum item. */ min_index = right_index; while (tree_array[min_index << 1] != null) { min_index <<= 1; } tree_array[item_index] = tree_array[min_index]; min_index_right_child = (min_index << 1) + 1; /* * If the minimum item had a right child, then replace the minimum * item with its right child. */ if (tree_array[min_index_right_child] != null) { tree_array[min_index] = tree_array[min_index_right_child]; tree_array[min_index_right_child] = null; shifted_index = 0; } else { tree_array[min_index] = null; if (levelForIndex(min_index) == current_level) shifted_index = 0; else shifted_index = min_index; } } /* * If the shifted index is 0 then we don't have a hole to fill, * otherwise, we have to find an active node on the current level, * and shift to fill the hole. */ if (shifted_index != 0) inorderShift(nearestActiveIndexForIndex(shifted_index), shifted_index); } size--; /* * Finally, we have to check if the deletion has decreased the level of * the tree. If so, we decrease the current level and reset the full * size. */ previous_level_full_size = (2 << (current_level - 1)) - 1; if (size <= previous_level_full_size) { current_level--; full_size = previous_level_full_size; } return true; } public synchronized void deleteAll() { int i; for (i = 0; i < tree_array.length; i++) tree_array[i] = null; full_size = 1; size = 0; current_level = 0; } private final int levelForIndex(int index) { int level_count = -1; /* * The level for an index can be calculated by repeatedly dividing the * index by two, and each time the result is not zero, we increment a * level count. */ do { index >>>= 1; level_count++; } while (index != 0); return level_count; } private final int beginIndexForLevel(int level) { /* * The beginning index for a level is calculated by multiplying 1 by 2 * level number of times. */ return 1 << level; } private final int endIndexForLevel(int level) { /* * The end index for a level is calculated by multiplying 2 by 2 * level number of times, then subtracting 1. */ return (2 << level) - 1; } private final void inorderShift(int start_position, int end_position) { int start_position_parent = start_position >>> 1; int end_position_parent = end_position >>> 1; int current_position; int next_inorder_position; Object current_object; Object shifting_object; /* * If the end position is less than the parent of the start position we * shift from end_position to start_position backwards starting at * end_position. */ if (end_position < start_position_parent) { current_position = end_position; next_inorder_position = end_position; shifting_object = tree_array[start_position]; tree_array[start_position] = null; /* Calculate next inorder position (for leaf). */ while ( (next_inorder_position & 0x00000001) == 1) next_inorder_position >>>=1; next_inorder_position >>>= 1; /* Enter the shift loop. */ while (next_inorder_position != start_position_parent) { /* Shift node. */ tree_array[current_position] = tree_array[next_inorder_position]; current_position = next_inorder_position; /* Calculate next inorder position. */ next_inorder_position = nextInorderForIndex(next_inorder_position, end_position); } /* Now move the final nodes. */ if ( (start_position & 0x00000001) == 1) { tree_array[current_position] = tree_array[start_position_parent]; tree_array[start_position_parent] = shifting_object; } else tree_array[current_position] = shifting_object; } /* * If the end position is greater then the parent of the start position * then we shift from start_position to end_position starting at * start_position. */ else { current_position = start_position; next_inorder_position= start_position; /* Calculate next inorder position (for leaf). */ while ( (next_inorder_position & 0x00000001) == 1) next_inorder_position >>>=1; next_inorder_position >>>= 1; shifting_object = tree_array[start_position]; tree_array[start_position] = null; /* Enter shift loop. */ while (next_inorder_position != end_position_parent) { current_object = tree_array[next_inorder_position]; tree_array[next_inorder_position] = shifting_object; shifting_object = current_object; current_position = next_inorder_position; next_inorder_position = nextInorderForIndex(next_inorder_position, end_position); } /* Now move the final nodes. */ if ( (end_position & 0x00000001) == 1) { tree_array[end_position] = tree_array[end_position_parent]; tree_array[end_position_parent] = shifting_object; } else tree_array[end_position] = shifting_object; } } private final int nextInorderForIndex(int index, int ep) { /* Leaf condition. */ if ( (tree_array[index << 1] == null) && (tree_array[(index << 1) + 1] == null) ) { while ( (index & 0x00000001) == 1) index >>>= 1; index >>>= 1; } else /* Interior node condition. */ { index = (index << 1) + 1; if (tree_array[index] == null) { while ( (index & 0x00000001) == 1) index >>>= 1; index >>>= 1; return index; } while (tree_array[index << 1] != null) index <<= 1; } return index; } private final int nearestActiveIndexForIndex(int start_index) { int i; int j; int level_begin; int level_end; int left_index; int right_index; int start_index_level; left_index = start_index << 1; right_index = left_index + 1; start_index_level = levelForIndex(start_index); level_begin = beginIndexForLevel(current_level); level_end = endIndexForLevel(current_level); if (start_index_level == current_level) { for (i = level_begin; i <= level_end; i++) { if (tree_array[i] != null) return i; } } else { for (i = start_index_level + 1; i < current_level; i++) { left_index = (left_index << 1) + 1; right_index = (right_index << 1) + 1; } if (tree_array[left_index] != null) return left_index; if (tree_array[right_index] != null) return right_index; /* Search the indexes between the ideal indexes. */ for (i = left_index + 1, j = right_index - 1; i < j; i++, j--) { if (tree_array[i] != null) return i; if (tree_array[j] != null) return j; } /* Search the indexes to the left and right sides. */ i = left_index - 1; j = right_index + 1; while (true) { if (i >= level_begin) { if (tree_array[i] != null) return i; } if (j <= level_end) { if (tree_array[j] != null) return j; } i--; j++; } } return 0; } private final void verifyCapacity() { int i; int desired_size; Object temp[]; desired_size = 2 << (current_level + 1); if (desired_size > current_capacity) { if ((desired_size - current_capacity) > capacity_increment) capacity_increment = desired_size - current_capacity; temp = new Object[current_capacity + capacity_increment]; for (i = 1; i < current_capacity; i++) temp[i] = tree_array[i]; tree_array = temp; current_capacity += capacity_increment; } } /** * Compares newItem to currentItem. * * @param newItem Item that is being inserted into the tree. * @param currentItem Item from the tree that newItem is being compared with. * @return -1, 0 or 1 to indicate that newItem is less than, equal, or greater than currentItem. */ abstract public int compare(Object newItem, Object currentItem); }