Mega Code Archive

 
Categories / Java / Collections Data Structure
 

Search Tree

//package termproject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer; /**  * (2,4) Search Tree  *  * Description: An ADT for a (2,4) Search Tree as described on page 451 of  * the Data Structures and Algorithms in Java textbook.  *  * Date Created: December, 2008  * @author Alex Laird, Wes Perrien  * @version 1.0  */ public class TwoFourTree implements Dictionary {   private Comparator treeComp;   private int size = 0;   private TFNode treeRoot = null;   /**    * Default constructor which assigns the comparator for the tree.    *     * @param comp Comparator the comparator to be assigned    */   public TwoFourTree(Comparator comp)   {     treeComp = comp;   }   /**    * Returns the current size of the tree.    *     * @return int the current size of the tree.    */   public int size()   {     return size;   }   /**    * Returns true if the tree is empty, otherwise false.    *     * @return boolean whether the tree is empty or not    */   public boolean isEmpty()   {     return (size == 0);   }   /**    * Returns a pointer to the root node of the tree.    *     * @return TFNode the root node    */   public TFNode root()   {       return treeRoot;   }   /**    * Finds an the first element in the tree with the specified key.    *     * @param key Object the key that is to be found in the tree    * @return Object the Item that contains the specified key    * @throws ElementNotFoundException if the key is not found in the tree    */   public Object findElement (Object key) throws ElementNotFoundException   {       TFNode node = firstGreaterOrEqualNode(key, root());       int itemIndex = firstGreaterOrEqualItem(key, node);              if(itemIndex == -1)           throw new ElementNotFoundException();       if(treeComp.isEqual(key, ((Item) node.getItem(itemIndex)).key()))           return node.getItem(itemIndex);       else           throw new ElementNotFoundException();   }   /**    * Inserts an Item with the specified key and element into the tree at it's    * proper location. This function determines the in order successor based on    * the specified key to find the proper location.    *     * @param key Object the key to be used for the inserted Item    * @param element Object the element to be used for the inserted Item    */   public void insertElement (Object key, Object element)   {       Item newItem = new Item(key, element);       // if this is the first element, make a root node       if(size() == 0)       {           treeRoot = new TFNode();           treeRoot.addItem(0, newItem);           size++;           return;       }       // find the node that contains the first greater or equal item       TFNode greatest = findInsertionPointer(newItem.key(), root());       if(greatest != null)       {           // find greatest or equal item within node           int index = firstGreaterOrEqualItem(newItem.key(), greatest);           // if a greater or equal item was not found in node, add at end           if(index == -1)               index = greatest.numItems();           // insert item at proper location           greatest.insertItem(index, newItem);       }       // if a greater or equal node was not found, insert at root       else       {           greatest = treeRoot;           // walks to the far right child           while(greatest.getChild(greatest.numItems()) != null)               greatest = greatest.getChild(greatest.numItems());           greatest.addItem(greatest.numItems(), newItem);       }       // incremet tree size       size++;       // if the insert caused a node to be greater than the max allowed size       // a node split is required       if(greatest.numItems() == greatest.maxItems() + 1)       {           nodePop(greatest);       }   }   /**    * Removes the first element from the tree that matches the specified key.    *     * @param key Object the key to be removed by the first found element    * @return Object the Item that contains the specified key    * @throws ElementNotFoundException if the key is not found in the tree    */   public Object removeElement (Object key) throws ElementNotFoundException   {       // ensure that the element is in the tree and find it       findElement(key);       TFNode node = firstGreaterOrEqualNode(key, root());       TFNode inOrderSuccessor = null;       Object element;       // find the index of the element within the node       int location = firstGreaterOrEqualItem(key, node);       // if the node is internal       if(node.getChild(location + 1) != null)       {           inOrderSuccessor = node.getChild(location + 1);                      // find the in order successor           while(inOrderSuccessor.getChild(0) != null)               inOrderSuccessor = inOrderSuccessor.getChild(0);           node.insertItem(location, inOrderSuccessor.removeItem(0));                      location++;       }       // remove the item from the tree       element = node.removeItem(location);       // rebalance the tree if the node has become empty       if(node.numItems() == 0          && node != root())           rebalance(node);              // rebalance tree if in order successor has become empty       if(inOrderSuccessor != null          && inOrderSuccessor.numItems() == 0)           rebalance(inOrderSuccessor);       return element;   }   /**    * Determines whether the specified node has a left sibling or not. If it does    * have a left sibling, this method returns the index to the left sibling.    *     * @param node TFNode the node to be checked for a left sibling    * @return int -1 if there is no left sibling, otherwise the index of the left sibling    */   protected int hasLeftSibling(TFNode node)   {       int childIndex;       // find the index in the parent of the current node       for(childIndex = 0; childIndex < 4; childIndex++)       {           if (node.getParent().getChild(childIndex) == node)               break;       }       if(childIndex != 0          && node.getParent().getChild(childIndex - 1) != null)          // return the index of the left sibling          return childIndex - 1;       else         // no left sibling exists         return -1;   }   /**    * Determines whether the specified node has a right sibling or not. If it does    * have a right sibling, this method returns the index to the right sibling.    *     * @param node TFNode the node to be checked for a right sibling    * @return int -1 if there is no right sibling, otherwise the index of the right sibling    */   protected int hasRightSibling(TFNode node)   {       int childIndex;       // find the index in the parent of the current node       for(childIndex = 0; childIndex < 4; childIndex++)       {           if (node.getParent().getChild(childIndex) == node)               break;       }       if(childIndex != 3          && node.getParent().getChild(childIndex + 1) != null)         // return the index of the right sibling         return childIndex + 1;       else         // no right sibling exists         return -1;   }   /**    * This method will rebalance if needed, usually directly following a remove.    *     * @param node TFNode the node to be the starting point for rebalance    */   protected void rebalance(TFNode node)   {       // check siblings       int leftSibIndex = hasLeftSibling(node);       int rightSibIndex = hasRightSibling(node);       TFNode leftSib = null;       TFNode rightSib = null;       TFNode parent = node.getParent();       TFNode newNode = null;              if(leftSibIndex != -1)       {           // get the left sibling         leftSib = parent.getChild(leftSibIndex);         }       if(rightSibIndex != -1)       {           // get the right sibling         rightSib = parent.getChild(rightSibIndex);       }              // transfer from left sibling       if(leftSibIndex != -1          && leftSib.numItems() > 1)       {         // insert parent into index 0         node.insertItem(0, parent.getItem(leftSibIndex));                  // make left sibling's most right child the first child here         node.setChild(0, leftSib.getChild(leftSib.numItems()));                    if(node.getChild(0) != null)           node.getChild(0).setParent(node);                    // store the last pointer reference           TFNode pointer = leftSib.getChild(leftSib.numItems() - 1);                    // make left sibling's greatest item the new parent         parent.replaceItem(leftSibIndex, leftSib.removeItem(leftSib.numItems() - 1));                      // reset the last pointer           leftSib.setChild(leftSib.numItems(), pointer);       }       // transfer from right sibling       else if(rightSibIndex != -1             && rightSib.numItems() > 1)       {         // insert parent into index 0         node.insertItem(0, parent.getItem(rightSibIndex - 1));                  // make right sibling's first child the last child here         node.setChild(0, rightSib.getChild(0));         if(node.getChild(0) != null)                 node.getChild(0).setParent(node);                    // store the third last pointer reference           TFNode pointer = rightSib.getChild(0);                    // make right sibling's first item the new parent         parent.replaceItem(rightSibIndex - 1, rightSib.removeItem(0));                      // reset the last pointer           rightSib.setChild(rightSib.numItems(), pointer);       }       // neither sibling has enough elements       else       {         // left fusion           if(leftSib != null              && leftSib.numItems() == 1)           {               // store nodes pointer               TFNode pointer = node.getChild(0);                              if(treeComp.isLessThan(((Item) leftSib.getItem(0)).key(), ((Item) parent.getItem(leftSibIndex)).key()))             leftSib.addItem(1, parent.removeItem(leftSibIndex));               else             leftSib.insertItem(0, parent.removeItem(leftSibIndex));                            // reset the last pointer               leftSib.setChild(leftSib.numItems(), pointer);                              if(pointer != null)                 pointer.setParent(leftSib);                              parent.setChild(leftSibIndex, leftSib);               newNode = leftSib;           }           // right fusion           else           {               // store the nodes pointer               TFNode pointer = node.getChild(rightSib.numItems());                              if(treeComp.isLessThan(((Item) rightSib.getItem(0)).key(), ((Item) parent.getItem(rightSibIndex - 1)).key()))                    rightSib.addItem(1, node.getParent().removeItem(rightSibIndex - 1));               else                    rightSib.insertItem(0, node.getParent().removeItem(rightSibIndex - 1));                            // reset the first pointer               rightSib.setChild(0, pointer);                              if(pointer != null)                   pointer.setParent(rightSib);                              parent.setChild(rightSibIndex - 1, rightSib);               newNode = rightSib;           }       }              // rebalance parent recursively if it has zero items and is not he parent       if(parent.numItems() == 0)       {           if(parent != root())              rebalance(parent);           else           {              treeRoot = newNode;              newNode.setParent(null);                            size--;           }       }              node = null;   }   /**    * Searches the tree until it finds a node with an item that is greater than    * or equal to the specified key.    *     * @param key Object the key to be compared against    * @param node TFNode the node to be the starting point, usually the root    * @return TFNode the node that holds the first greater or equal item    */   protected TFNode firstGreaterOrEqualNode(Object key, TFNode start)   {       // recursive exit condition       if(start == null)           return null;              int index = firstGreaterOrEqualItem(key, start);              if(index == -1)       {           index = start.numItems();           return firstGreaterOrEqualNode(key, start.getChild(index));       }              if(treeComp.isLessThan(key, ((Item) start.getItem(index)).key()))          return firstGreaterOrEqualNode(key, start.getChild(index));       else if(treeComp.isGreaterThan(key, ((Item) start.getItem(index)).key()))               return firstGreaterOrEqualNode(key, start.getChild(index + 1));       else           return start;   }      /**    * Searches the node until it finds a node that is greater than or equal to    * the specified key.    *     * @param key Object the key to be compared against    * @param node TFNode the node to be searched    * @return int the index of the first greater than or equal item    */   protected int firstGreaterOrEqualItem(Object key, TFNode node) throws ElementNotFoundException   {       if(node == null)           throw new ElementNotFoundException();              // find first greater or equal item within the node       for(int i = 0; i < node.numItems(); i++)       {           if (treeComp.isGreaterThanOrEqualTo(((Item) node.getItem(i)).key(), key))               return i;       }       // no greater or equal item was found       return -1;   }   /**    * Searches the tree until it finds a node with an item that is greater than    * or equal to the specified key.  This is the point at which it will insert    * the a new node.    *     * @param key Object the key to be compared against    * @param start TFNode the node to be the starting point, usually the root    * @return TFNode the node that holds the first greater or equal item    */   protected TFNode findInsertionPointer(Object key, TFNode start)   {       // exit condition for recursion       if(start == null)           return null;       int index = firstGreaterOrEqualItem(key, start);        // if a greater or equal item was not found in node, add at end       if(index == -1)           index = start.numItems();       // continue searching down tree       TFNode node = findInsertionPointer(key, start.getChild(index));       if(node == null)           return start;       return node;   }   /**    * Pops the third item out of a node and sends it to the parent, thus creating    * the children nodes off of that parent then. Essentially a node split. Usually    * called directly after an insert if the insert causes a node to become too full.    *     * @param left TFNode the node to be split    */   protected void nodePop(TFNode left)   {       TFNode parent;       TFNode right = new TFNode();       int index;       // if no parent exists, we are the root       if(left.getParent() == null)       {           parent = new TFNode();           treeRoot = parent;           index = 0;       }       else       {           parent = left.getParent();           index = firstGreaterOrEqualItem(((Item) left.getItem(2)).key(), parent);           // if a greater or equal item was not found in node, add at end           if(index == -1)               index = parent.numItems();       }       // insert items in new locations       parent.insertItem(index, left.getItem(2));       right.addItem(0, left.getItem(3));       // set new child pointers       parent.setChild(index, left);       parent.setChild(index + 1, right);       // set parent pointers       left.setParent(parent);       right.setParent(parent);       // conditions if the node has more than three children       if(left.getChild(3) != null)       {           right.setChild(0, left.getChild(3));           right.getChild(0).setParent(right);       }       if(left.getChild(4) != null)       {           right.setChild(1, left.getChild(4));           right.getChild(1).setParent(right);       }       // store the third pointer reference       TFNode pointer = left.getChild(2);       // remove the two items       left.removeItem(2);       left.removeItem(2);       // reassign the child which was lost in the remove       left.setChild(2, pointer);       // check to see if the parent is too full now; call recursion       if(parent.numItems() == parent.maxItems() + 1)           nodePop(parent);   }   /**    * The main method which the program executes from and which handles all    * testing and input/output.    *     * @param args String[]    */   public static void main(String[] args) throws IOException   {         Comparator myComp = new IntegerComparator();         TwoFourTree myTree = new TwoFourTree(myComp);         TwoFourTree firstTree = new TwoFourTree(myComp);         TwoFourTree secondTree = new TwoFourTree(myComp);         BufferedReader myReader = new BufferedReader(new InputStreamReader(System.in));         String input;         boolean go = true;              while(go == true)         {             System.out.print("Format like this:  -a 37 to add a 37 to the tree. -r 37 to remove a 37 from the tree: ");             input = myReader.readLine();                          if(input.substring(0, 2).equals("-a"))             {                 Integer myInt = new Integer(input.substring(3, input.length()));                 myTree.insertElement(myInt, myInt);                 myTree.printAllElements();             }             else if(input.substring(0, 2).equals("-r"))             {                 Integer myInt = new Integer(input.substring(3, input.length()));                 myTree.removeElement(myInt);                 myTree.printAllElements();             }             else                 System.out.print("\nYou're bad!");                          System.out.print("\n");                          }                  /*System.out.println("Loading firstTree with values ...");                  Integer firstInt1 = new Integer(20);         firstTree.insertElement(firstInt1, firstInt1);         Integer firstInt2 = new Integer(50);         firstTree.insertElement(firstInt2, firstInt2);         Integer firstInt3 = new Integer(30);         firstTree.insertElement(firstInt3, firstInt3);         Integer firstInt4 = new Integer(40);         firstTree.insertElement(firstInt4, firstInt4);         Integer firstInt5 = new Integer(15);         firstTree.insertElement(firstInt5, firstInt5);         Integer firstInt6 = new Integer(35);         firstTree.insertElement(firstInt6, firstInt6);         Integer firstInt7 = new Integer(55);         firstTree.insertElement(firstInt7, firstInt7);         Integer firstInt8 = new Integer(38);         firstTree.insertElement(firstInt8, firstInt8);         Integer firstInt9 = new Integer(17);         firstTree.insertElement(firstInt9, firstInt9);         Integer firstInt10 = new Integer(37);         firstTree.insertElement(firstInt10, firstInt10);         Integer firstInt11 = new Integer(16);         firstTree.insertElement(firstInt11, firstInt11);         Integer firstInt12 = new Integer(36);         firstTree.insertElement(firstInt12, firstInt12);         Integer firstInt13 = new Integer(15);         firstTree.insertElement(firstInt13, firstInt13);         Integer firstInt14 = new Integer(36);         firstTree.insertElement(firstInt14, firstInt14);         Integer firstInt15 = new Integer(30);         firstTree.insertElement(firstInt15, firstInt15);         Integer firstInt16 = new Integer(34);         firstTree.insertElement(firstInt16, firstInt16);         Integer firstInt17 = new Integer(34);         firstTree.insertElement(firstInt17, firstInt17);         Integer firstInt18 = new Integer(30);         firstTree.insertElement(firstInt18, firstInt18);         Integer firstInt19 = new Integer(30);         firstTree.insertElement(firstInt18, firstInt18);                 System.out.println("firstTree is fully loaded as follows:");         firstTree.printAllElements();         System.out.println("Checking parent/child connections ...");         firstTree.checkTree(firstTree.root());         System.out.println("All parent/child connections valid.");                  System.out.println("\nRemove 30");         firstTree.removeElement(30);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 30");         firstTree.removeElement(30);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 30");         firstTree.removeElement(30);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 30");         firstTree.removeElement(30);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 15");         firstTree.removeElement(15);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 55");         firstTree.removeElement(55);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 35");         firstTree.removeElement(35);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 38");         firstTree.removeElement(38);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 40");         firstTree.removeElement(40);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 36");         firstTree.removeElement(36);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 17");         firstTree.removeElement(17);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 34");         firstTree.removeElement(34);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 34");         firstTree.removeElement(34);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 36");         firstTree.removeElement(36);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 20");         firstTree.removeElement(20);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 37");         firstTree.removeElement(37);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 16");         firstTree.removeElement(16);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 50");         firstTree.removeElement(50);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("");         System.out.println("Remove 15");         firstTree.removeElement(15);         System.out.println("Removed successfully. Print new tree:");         firstTree.printAllElements();         System.out.println("done");         System.out.println("\n\nLoading secondTree with values ...");                  Integer secondInt1 = new Integer(47);         secondTree.insertElement(secondInt1, secondInt1);         Integer secondInt2 = new Integer(83);         secondTree.insertElement(secondInt2, secondInt2);         Integer secondInt3 = new Integer(22);         secondTree.insertElement(secondInt3, secondInt3);         Integer secondInt4 = new Integer(16);         secondTree.insertElement(secondInt4, secondInt4);         Integer secondInt5 = new Integer(49);         secondTree.insertElement(secondInt5, secondInt5);         Integer secondInt6 = new Integer(100);         secondTree.insertElement(secondInt6, secondInt6);         Integer secondInt7 = new Integer(38);         secondTree.insertElement(secondInt7, secondInt7);         Integer secondInt8 = new Integer(3);         secondTree.insertElement(secondInt8, secondInt8);         Integer secondInt9 = new Integer(53);         secondTree.insertElement(secondInt9, secondInt9);         Integer secondInt10 = new Integer(66);         secondTree.insertElement(secondInt10, secondInt10);         Integer secondInt11 = new Integer(19);         secondTree.insertElement(secondInt11, secondInt11);         Integer secondInt12 = new Integer(23);         secondTree.insertElement(secondInt12, secondInt12);         Integer secondInt13 = new Integer(24);         secondTree.insertElement(secondInt13, secondInt13);         Integer secondInt14 = new Integer(88);         secondTree.insertElement(secondInt14, secondInt14);         Integer secondInt15 = new Integer(1);         secondTree.insertElement(secondInt15, secondInt15);         Integer secondInt16 = new Integer(97);         secondTree.insertElement(secondInt16, secondInt16);         Integer secondInt17 = new Integer(94);         secondTree.insertElement(secondInt17, secondInt17);         Integer secondInt18 = new Integer(35);         secondTree.insertElement(secondInt18, secondInt18);         Integer secondInt19 = new Integer(51);         secondTree.insertElement(secondInt19, secondInt19);                  System.out.println("firstTree is fully loaded as follows:");         secondTree.printAllElements();         System.out.println("Checking parent/child connections ...");         secondTree.checkTree(secondTree.root());         System.out.println("All parent/child connections valid.");                  System.out.println("\nRemove 19");         secondTree.removeElement(19);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 3");         secondTree.removeElement(3);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 66");         secondTree.removeElement(66);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 88");         secondTree.removeElement(88);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 94");         secondTree.removeElement(94);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 100");         secondTree.removeElement(100);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 97");         secondTree.removeElement(97);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 47");         secondTree.removeElement(47);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 53");         secondTree.removeElement(53);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 38");         secondTree.removeElement(38);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 24");         secondTree.removeElement(24);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 83");         secondTree.removeElement(83);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 49");         secondTree.removeElement(49);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 23");         secondTree.removeElement(23);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 51");         secondTree.removeElement(51);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 1");         secondTree.removeElement(1);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 22");         secondTree.removeElement(22);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 16");         secondTree.removeElement(16);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("");         System.out.println("Remove 35");         secondTree.removeElement(35);         System.out.println("Removed successfully. Print new tree:");         secondTree.printAllElements();         System.out.println("done");*/   }   public void printAllElements() {     int indent = 0;     if (root() == null) {       System.out.println("The tree is empty");     }     else {       printTree(root(), indent);     }   }   public void printTree(TFNode start, int indent) {     if (start == null) {       return;     }     for (int i = 0; i < indent; i++) {       System.out.print(" ");     }     printTFNode(start);     indent += 4;     int numChildren = start.numItems() + 1;     for (int i = 0; i < numChildren; i++) {       printTree(start.getChild(i), indent);     }   }   public void printTFNode(TFNode node) {     int numItems = node.numItems();     for (int i = 0; i < numItems; i++) {       System.out.print( ( (Item) node.getItem(i)).element() + " ");     }     System.out.println();   }   // checks if tree is properly hooked up, i.e., children point to parents   public void checkTree(TFNode start) {     if (start == null) {       return;     }     if (start.getParent() != null) {       TFNode parent = start.getParent();       int childIndex = 0;       for (childIndex = 0; childIndex <= parent.numItems(); childIndex++) {         if (parent.getChild(childIndex) == start) {           break;         }       }       // if child wasn't found, print problem       if (childIndex > parent.numItems()) {         System.out.println("Child to parent confusion");         printTFNode(start);       }     }     if (start.getChild(0) != null) {       for (int childIndex = 0; childIndex <= start.numItems(); childIndex++) {         if (start.getChild(childIndex).getParent() != start) {           System.out.println("Parent to child confusion");           printTFNode(start);         }       }     }     int numChildren = start.numItems() + 1;     for (int childIndex = 0; childIndex < numChildren; childIndex++) {       checkTree (start.getChild(childIndex));     }   } } /**  * Basic storage element for the 2-4 Tree  *  * @author Dr. Gallagher  * @version 1.0  * Created 2 Mar 2001  * Description: The basic node for a 2-4 tree.  Contains an array of Items,  * an array of references to children TFNodes, a pointer to a parent TFNode,  * and a count of how many Items are stored in the node.  */ class TFNode {     private static final int MAX_ITEMS = 3;     private int numItems = 0;     private TFNode nodeParent;     private TFNode[] nodeChildren;     private Object[] nodeItems;     public TFNode() {             // make them one bigger than needed, so can handle oversize nodes             // during inserts         nodeChildren = new TFNode[MAX_ITEMS+2];         nodeItems = new Object[MAX_ITEMS+1];     }     public int numItems () {         return numItems;     }     public int maxItems() {         return MAX_ITEMS;     }     public TFNode getParent() {         return nodeParent;     }     public void setParent (TFNode parent) {         nodeParent = parent;     }     public Object getItem(int index) {         if ( (index < 0) || (index > (numItems-1) ) )             throw new TFNodeException();         return nodeItems[index];     }         // adds, but does not extend array; so it overwrites anything there     public void addItem (int index, Object data) {             // always add at end+1; check that you are within array         if ( (index < 0) || (index > numItems) || (index > MAX_ITEMS) )             throw new TFNodeException();         nodeItems[index] = data;         numItems++;     }         // this function inserts an item into the node, and adjusts into child         // pointers to add the proper corresponding pointer     public void insertItem (int index, Object data) {         if ( (index < 0) || (index > numItems) || (index > MAX_ITEMS) )             throw new TFNodeException();             // adjust Items         for (int ind=numItems; ind > index; ind--) {             nodeItems[ind] = nodeItems[ind-1];         }             // insert new data into hole made         nodeItems[index] = data;             // adjust children pointers; if inserting into index=1, we make             // pointers 1 and 2 to point to 1; this is because whoever called             // this function will fix one of them later; index 0 doesn't change;             // pointer 3 becomes pointer 2; pointer 4 becomes 3, etc.         for (int ind=numItems+1; ind > index; ind--) {             nodeChildren[ind] = nodeChildren[ind-1];         }         numItems++;     }         // this method removes item, and shrinks array     public Object removeItem (int index) {         if ( (index < 0) || (index > (numItems-1) ) )             throw new TFNodeException();         Object removedItem = nodeItems[index];         for (int ind=index; ind < numItems-1; ind++) {             nodeItems[ind] = nodeItems[ind+1];         }         nodeItems[numItems-1] = null;             // fix children pointers also             // typically, you wouldn't expect to do a removeItem unless             // children are null, because removal of an item will mess up the             // pointers; however, here we will simply delete the child to the             // left of the removed item; i.e., the child with same index         for (int ind=index; ind < numItems; ind++) {             nodeChildren[ind] = nodeChildren[ind+1];         }         nodeChildren[numItems] = null;         numItems--;         return removedItem;     }         // this method removes item, but does not shrink array     public Object deleteItem (int index) {         if ( (index < 0) || (index > (numItems-1) ) )             throw new TFNodeException();         Object removedItem = nodeItems[index];         nodeItems[index] = null;         numItems--;         return removedItem;     }         // replaces Item at index with newItem, returning the old Item     public Object replaceItem (int index, Object newItem) {         if ( (index < 0) || (index > (numItems-1) ) )             throw new TFNodeException();         Object returnItem = nodeItems[index];         nodeItems[index] = newItem;         return returnItem;     }     public TFNode getChild (int index) {         if ( (index < 0) || (index > (MAX_ITEMS+1)) )             throw new TFNodeException();         return nodeChildren[index];     }     public void setChild (int index, TFNode child) {         if ( (index < 0) || (index > (MAX_ITEMS+1)) )             throw new TFNodeException();         nodeChildren[index] = child;     } } class TFNodeException extends RuntimeException {     public TFNodeException() {         super ("Problem with TFNode");     }     public TFNodeException(String errorMsg) {         super (errorMsg);     } } interface Dictionary {     public int size();     public boolean isEmpty();     public Object findElement (Object key) throws ElementNotFoundException;     public void insertElement (Object key, Object element);     public Object removeElement (Object key) throws ElementNotFoundException; } class ElementNotFoundException  extends RuntimeException {     public ElementNotFoundException()     {     } } class Item {     private Object itemKey;     private Object itemElement;     public Item() {         this (null, null);     }     public Item(Object key, Object element) {         itemKey = key;         itemElement = element;     }     public Object key() {         return itemKey;     }     public void setKey(Object key) {         itemKey = key;     }     public Object element() {         return itemElement;     }     public void setElement (Object element) {         itemElement = element;     } }interface Comparator {     public boolean isLessThan (Object obj1, Object obj2);     public boolean isLessThanOrEqualTo (Object obj1, Object obj2);     public boolean isGreaterThan (Object obj1, Object obj2);     public boolean isGreaterThanOrEqualTo (Object obj1, Object obj2);     public boolean isEqual (Object obj1, Object obj2);     public boolean isComparable (Object obj); }class IntegerComparator implements Comparator {     public IntegerComparator() {     }     public boolean isLessThan (Object obj1, Object obj2) {         Integer myInt1;         Integer myInt2;         try {             myInt1 = (Integer) obj1;             myInt2 = (Integer) obj2;         }         catch (ClassCastException exc) {             throw new InvalidIntegerException ("Object not an integer");         }         return ( myInt1.intValue() < myInt2.intValue() );     }     public boolean isLessThanOrEqualTo (Object obj1, Object obj2) {         return ( ! isLessThan (obj2, obj1) );     }     public boolean isGreaterThan (Object obj1, Object obj2) {         return ( isLessThan (obj2, obj1) );     }     public boolean isGreaterThanOrEqualTo (Object obj1, Object obj2) {         return ( ! isLessThan (obj1, obj2) );     }     public boolean isEqual (Object obj1, Object obj2) {         return ( (! isLessThan (obj1, obj2)) && (! isLessThan (obj2, obj1)) );     }     public boolean isComparable (Object obj) {         try {             Integer myInt = (Integer) obj;             return true;         }         catch (ClassCastException exc) {             return false;         }     } } class InvalidIntegerException extends RuntimeException {     public InvalidIntegerException(String errorMsg) {         super (errorMsg);     } }