/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.binarySearchTrees; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import xxl.core.collections.queues.ListQueue; import xxl.core.collections.queues.Queue; import xxl.core.cursors.filters.WhileTaker; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Function; import xxl.core.predicates.AbstractPredicate; import xxl.core.predicates.Predicate; /** BinarySearchTree is a generic implementation of the functionality of binary search trees. The implementation uses some ideas that are described in: "Introduction to Algorithms", MIT Electrical Engineering and Computer Science, by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest. <br><br> Trees constructed using this class are not implicitly balanced. For balanced trees consider the classes AVLTree, BBTree and RedBlackTree. @see AVLTree @see BBTree @see RedBlackTree */ public class BinarySearchTree { /** * Returns a Factory-Method Function (Predicate x Function -> BinarySearchTree) that * constructs new BinarySearchTrees. */ public static final Function FACTORY_METHOD = new AbstractFunction() { public Object invoke (Object fixAggregate, Object fixRotation) { return new BinarySearchTree((Predicate)fixAggregate,(Function)fixRotation); } }; /** * Nodes in a BinarySearchTree are of the type Node. */ public class Node { /** * Object that is contained in the node. */ protected Object object; /** * Parent node of the current node. */ protected Node parent; /** * Nodearray that contains child nodes. */ protected Node [] children = new Node [] {null, null}; /** * Creates a new node. * @param object The object that is contained in the node. * @param parent The parent node of the node that becomes created. */ protected Node (Object object, Node parent) { this.object = object; this.parent = parent; } /** * Returns the object of the node. * * @return Object - object of the node. */ public Object object () { return object; } /** * Returns the parent node of the current node. * @return Object - parent node. */ public Node parent () { return parent; } /** Returns the left or right child. * @param right <code>true</code> for right child, <code>false</code> for left child * @return Node - returns the right(left) child if true(false) */ public Node child (boolean right) { return children[right? 1: 0]; } /** * Tests if the node is a leaf. * @return boolean - true if the current node is a leaf */ public boolean isLeaf () { return children[0]==children[1]; } /** * Returns 0(1) if this node is the left(right) son of its * parent. If the node is the root-node -1 is returned. * @return int - returns the index of the current node from the * parent point of view. */ public int index () { return parent==null? -1: parent.index(this); } /** * Returns the index of 'child'. If 'child' is not a child * of this node -1 is returned. * @param child the child whose index is searched * @return int - index of a child. */ public int index (Node child) { return child==this.children[1]? 1: child==this.children[0]? 0: -1; } /** * Returns the symmetric predecessor (index=0) or * or successor (index=1) for this node. * @param index 0 for symmetric predecessor or 1 for symmetric successor * @return Node - returns the symmetric predecessor or successor. */ public Node next (int index) { Node node = this; if (children[index]==null) { while (node.index()==index) node = node.parent; node = node.parent; } else { node = node.children[index]; index ^= 1; while (node.children[index]!=null) node = node.children[index]; } return node; } /** * This method is called when a structural change occures * (e.g. during an insert()- or remove()-operation) * in one of the subtrees. * In a BinarySearchTree-Object this method has nothing * to do. It is usually overwritten in subclasses. * @param index * @see AVLTree * @see RedBlackTree * @see BBTree */ protected void fix (int index) { } /** * This method has to be implemented in case that a * rotation invalidates information of the node. * This method should copy the information from this * node to the node <code>next</code>. * In a BinarySearchTree-Object this method has nothing * to do. It is usually overwritten in subclasses. * @param next destination for the rotation information. * @see AVLTree * @see RedBlackTree */ protected void designate (Node next) { } /** * This method is called for the son of the rotation, i.e. * after the rotation the son is the new father-node. * This method calls <code>fixRotation()</code> (for * the new son) and * <code>fixAggregate()</code> (for father and son). * It is assumed, that the aggregates of the parents of the * new root of the subtree are not affected. * @return Node - this */ protected Node rotate () { int index = index(); if ((parent.children[index] = children[index^1])!=null) children[index^1].parent = parent; parent.parent = (parent = (children[index^1] = parent).parent)==null? (root = this): (parent.children[parent.index(children[index^1])] = this); fixRotation.invoke(children[index^1]); fixAggregate.invoke(children[index^1]); fixAggregate.invoke(this); return this; } /** * Exactmatch query. Locates an object in the tree in the current node or below. * * @param chooseSubtree Comparator that is used to find the requested object. * @param object Object[] that contains the object. * @param result int[] that contains a return value (see "Returns"). * @return Node that contains the object or the node * where the search had to be terminated * (result[0] is set to 0 if the object was found, <0 if object is left of that * node, >0 if greater than the node) */ public Node get (Comparator chooseSubtree, Object [] object, int [] result) { Node node = this, child = node; while (child!=null && (result[0] = chooseSubtree.compare(object, node = child))!=0) child = node.children[result[0]>0? 1: 0]; return node; } /** * Inserts an object into the tree. This method locates the correct * position for the object in the tree and inserts the given object. * * @param chooseSubtree Comparator that is used to locate the place where the object * becomes inserted. * @param object Object[] that contains the object. * @return null if the tree did not contain the object. * Otherwise the node that already contains the object (if it is already in the tree) * is returned. * In the latter case there are no changes in the tree. */ public Node insert (Comparator chooseSubtree, Object [] object) { int [] result = new int [1]; Node node = get(chooseSubtree, object, result); if (result[0]==0) return node; else { node.insert(result[0]>0? 1: 0, object[0]); return null; } } /** * Appends an object to one of the children-nodes. * @param index Position where to insert the new node into the children array. * @param object contains the object. */ public void insert (int index, Object object) { children[index] = newNode(object, this); size++; for (Node node = this; node!=null && !fixAggregate.invoke(node);) node = node.parent; fix(index); } /** * Locates the object and removes it from the tree. * @param chooseSubtree Comparator that is used to locate the place where the object * is located. * @param object Object[] that contains the object. * @param nextIndex index of the object to be removed * @return null if the tree did not contain the object. * Otherwise the node that has contained the object before the remove-operation * is returned. */ public Node remove (Comparator chooseSubtree, Object [] object, int nextIndex) { int [] result = new int [1]; Node node = get(chooseSubtree, object, result); if (result[0]!=0) return null; else { node.remove(nextIndex); return node; } } /** * Removes the child node with number nextIndex from the current node. * This operation is implemented a little bit different compared to the remove * algorithm that is proposed in many books. It looks for the successor-node * and rotates him below the node ... * This method is more generic, because it works with every type of tree that * is subclassed from BinarySearchTree. * @param nextIndex */ public void remove (int nextIndex) { if (isLeaf()) if (parent==null) clear(); else { int index = index(); parent.children[index] = null; size--; for (Node node = parent; node!=null && !fixAggregate.invoke(node);) node = node.parent; parent.fix(index); } else { Node next = next(nextIndex ^= (children[nextIndex]==null? 1: 0)); int fixIndex = next.index(); Node toFix = fixIndex==(nextIndex ^= 1)? next.parent: next; designate(next); while (next.children[nextIndex]!=this) next.rotate(); while (children[nextIndex]!=null) children[nextIndex].rotate(); parent.children[index()] = null; size--; for (Node node = parent; node!=null && !fixAggregate.invoke(node);) node = node.parent; toFix.fix(fixIndex); } parent = null; } } /** * Root of the tree. */ protected Node root = null; /** * Size of the tree (number of nodes). */ protected int size = 0; /** * Updates aggregate-information of a node, i.e. the invariant for * that node is recomputed. This predicate is called with 1 argument: the node. * Checks if the aggregate-operation is complete or if * the parent has to be updated (only during insertion). * The predicate has to return true, iff the aggregate is fixed * and need not to be (re)calculated for the parent node. */ protected Predicate fixAggregate; /** * Updates rotation-information of a node. * This function is called with 1 argument: the node */ protected Function fixRotation; /** * Creates a BinarySearchTree. * * @param fixAggregate Predicate that is called when an aggregate information in * the tree might be outdated. * @param fixRotation Function that is called when a rotation has occured and rotation * information in a node has to become fixed. */ public BinarySearchTree (Predicate fixAggregate, Function fixRotation) { this.fixAggregate = fixAggregate; this.fixRotation = fixRotation; } /** * Creates and initializes a new node of the tree. The node is * inserted into the tree only if the parent is set correctly. * * @param object The object that is contained in the node. * @param parent The parent node of the node that becomes created. * @return Node - new node that contains the object object. */ public Node newNode (Object object, Node parent) { return new Node(object, parent); } /** * Deletes all elements of the tree. After this operation the tree * is empty. */ public void clear () { root = null; size = 0; } /** * Initialized the tree with exactly one node that contains the specified object. * Caution: if the tree is not empty, the previously contained nodes are removed. * After performing this operation the number of nodes in the tree is exactly 1. * * @param object The object that is inserted into the only node of the tree (root). */ public void init (Object object) { root = newNode(object, null); size = 1; } /** * Returns the root of the tree. * * @return Node - the root of the tree. */ public Node root () { return root; } /** * Returns the size of the tree (number of nodes) * * @return int - size of the tree */ public int size () { return size; } /** * Returns the first node due to the tree comparator. * * @return Node - the first node of the tree. */ public Node first () { Node node = root(); if (node!=null) while (node.children[0]!=null) node = node.children[0]; return node; } /** * Returns the last node due to the tree comparator. * * @return Node - the last node of the tree. */ public Node last () { Node node = root(); if (node!=null) while (node.children[1]!=null) node = node.children[1]; return node; } /** * Exactmatch query. Locates an object in the tree. * * @param chooseSubtree Comparator that is used to find the requested object. * @param object Object[] that contains the object. * @param result int[] that contains a return value (see "Returns"). * @return Node that contains the object or the node where the search had to * be terminated (result[0] is set to 0 if the object was found, <0 if object * is left of that node, >0 if greater than the node) */ public Node get (Comparator chooseSubtree, Object [] object, int [] result) { return root()==null? null: root().get(chooseSubtree, object, result); } /** * Inserts an object into the tree. * * @param chooseSubtree Comparator that is used to locate the place where the object * becomes inserted. * @param object Object[] that contains the object. * @return null if the tree did not contain the object, * otherwise the node that already contains the object (if it is already in the tree). * In the latter case there are no changes in the tree. */ public Node insert (Comparator chooseSubtree, Object [] object) { if (root()==null) { init(object[0]); return null; } else return root().insert(chooseSubtree, object); } /** * Search the object and remove it from the tree. * * @param chooseSubtree Comparator that is used to find the object. * @param object Object[] that contains the object. * @param nextIndex * @return null if the tree did not contain the object, otherwise the node * that has contained the object before the remove-Operation. */ public Node remove (Comparator chooseSubtree, Object [] object, int nextIndex) { return root()==null? null: root().remove(chooseSubtree, object, nextIndex); } /** * Returns an inorder forward (left to right) iterator of the objects of this tree. * @return Iterator - inorder forward iterator. */ public Iterator iterator () { return iterator(true); } /** * Returns an inorder iterator of the objects of this tree. * @param forwards <code>true</code>: forward (left to right), otherwise * backwards (right to left). * @return Iterator - specified inorder iterator. */ public Iterator iterator (boolean forwards) { return new InOrderIterator(forwards? first(): last(), forwards); } /** * Returns a level order iterator of the objects of this tree. * @return Iterator - level order iterator. */ public Iterator levelOrderIterator () { return new LevelOrderIterator(root); } /** * Returns an inorder iterator that contains the objects * that lie between minkey and maxkey (range query). * * @param chooseSubtree The comparator used to choose the right subtree * @param minkey defines the left border * @param maxkey defines the right border * @param forwards specifies the order the elements are returned * @return inorder iterator that contains the objects that lie between minkey and maxkey (range query) */ public Iterator rangeQuery(final Comparator chooseSubtree, final Object [] minkey, final Object [] maxkey, final boolean forwards) { int [] result = new int [1]; Node n = get(chooseSubtree,forwards?minkey:maxkey,result); // object not found if (forwards && result[0]>0) n = n.next(1); else if (!forwards && result[0]<0) n = n.next(0); return new WhileTaker( new InOrderIterator(n,forwards), new AbstractPredicate() { public boolean invoke (Object next) { return ((forwards?1:-1)*chooseSubtree.compare(forwards?maxkey:minkey,next)>=0); } }, true ); } /** * Inner class implementing inorder traversal through the tree. * The traversal can start at any node of the tree that has to * be passed to the constructor. */ public class InOrderIterator implements Iterator { /** * needed for remove operation. */ protected Node node = null; /** * next element of the iteration. */ protected Node next; /** * needed for remove operation. */ protected int nextIndex; /** * Constructs an InOrderIterator that starts at the * given node. * @param first the node where the InOrderIterator starts * its iteration. * @param forwards specifies the order the elements are returned. */ public InOrderIterator (Node first, boolean forwards) { this.next = first; this.nextIndex = forwards? 1: 0; } /** * Returns <tt>true</tt> if the iteration has more elements. * (In other words, returns <tt>true</tt> if <tt>next</tt> or <tt>peek</tt> would * return an element rather than throwing an exception.). * Implements java.util.Iterator.hasNext(). * * @return <tt>true</tt> if the traversal has more elements. */ public boolean hasNext () { return next!=null; } /** * Returns the next element in the iteration. <br> * The tree is not changed. * * @return the next element in the iteration. * @throws java.util.NoSuchElementException if the iteration has no more elements. */ public Object next () throws NoSuchElementException { if (!hasNext()) throw new NoSuchElementException(); else { // return next and // calculate the node of the iteration after "next" next = (node = next).next(nextIndex); return node; } } /** * Removes an object from the tree. After calling this operation, * the iterator gets invalid and does not return elements * of the traversal. * * @throws IllegalStateException */ public void remove () throws IllegalStateException { if (node==null) throw new IllegalStateException(); node.remove(nextIndex^1); node = null; } } /** * Inner class implementing level order traversal through the tree. * During a level order traversal, nodes have to be stored in * memory. This is done in a ListQueue internally. The queue can * contain |leaf nodes| nodes at maximum.<br> * Currently, the remove operation is not supported (throws an * UnsupportedOperationExcetion). */ public class LevelOrderIterator implements Iterator { /** * Needed for remove (for future implementation). */ protected Node node = null; /** * Next element of the iteration. */ protected Node next; /** * Data structure used to store nodes. */ protected Queue q; /** * Constructs a LevelOrderIterator that starts at the * given node. * @param first The node where the LevelOrderIterator starts * its iteration. */ public LevelOrderIterator (Node first) { this.next = first; q = new ListQueue(); } /** * Returns <tt>true</tt> if the iteration has more elements. * (In other words, returns <tt>true</tt> if <tt>next</tt> or <tt>peek</tt> would * return an element rather than throwing an exception.). * Implements java.util.Iterator.hasNext(). * * @return <tt>true</tt> if the traversal has more elements. */ public boolean hasNext () { return next!=null; } /** * Internally used to put the child nodes into the queue. * * @param cur the node whose child nodes should be put into * the queue. */ private void refillQueue(Node cur) { Node n; if ((n = cur.children[0])!=null) q.enqueue(n); if ((n = cur.children[1])!=null) q.enqueue(n); } /** * Returns the next element in the iteration. <br> * The tree is not changed. * * @return the next element in the iteration. * @throws java.util.NoSuchElementException if the iteration has no more elements. */ public Object next () throws NoSuchElementException { if (!hasNext()) throw new NoSuchElementException(); else { refillQueue(next); node = next; if (!q.isEmpty()) next = (Node) q.dequeue(); else next = null; return node; } } /** * Currently not supported. Should remove an object from the tree. * * @throws IllegalStateException */ public void remove () throws IllegalStateException { throw new UnsupportedOperationException(); } } }