/* 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.ArrayList; import java.util.Comparator; import java.util.List; import xxl.core.collections.Lists; import xxl.core.util.Interval1D; /** * A simple static Priority Serach Tree. * * - all event point have to be known on construction time of the tree. * - no event point may occur twice as right border of an interval in the tree. */ public class PrioritySearchTree <T> { /** * A node of the Priority Search Tree. * */ protected class Node { /** * The left son of the node. */ protected Node left; /** * The right son of the node. */ protected Node right; /** * The split value of the node. */ protected T split; /** * The interval stored in the node. */ protected Interval1D interval; /** * Creates a new node. * * @param left the left son of the new node. * @param right the right son of the new node. * @param split the split value of the new node. * @param interval the interval stored in the node. */ public Node (Node left, Node right, T split, Interval1D interval) { this.left = left; this.right = right; this.split = split; this.interval = interval; } /** * Creates a new node without interval. * * @param left the left son of the new node. * @param right the right son of the new node. * @param split the split value of the new node. */ public Node (Node left, Node right, T split) { this(left, right, split, null); } /** * Creates a new leaf node without interval. * * @param split the split value of the new node. */ public Node (T split) { this(null, null, split, null); } /** * Moves the left split values for all nodes in the subtree up one level. */ protected void leftup() { if (left==null) return; split = left.split; left.leftup(); if (right!=null) right.leftup(); } } /** * The comparator used to compare elements of the underlying type T. */ public final Comparator<T> comparator; /** * The root node of the tree. */ private Node root; /** * Creates a new Priority Search Tree for a set of event points. * * @param events a list of the event points to use in the tree. * @param isSorted a flag indicating if the event points are sorted. * @param comparator a comparator for elements of the underlying type T. */ public PrioritySearchTree (ArrayList<T> events, boolean isSorted, Comparator<T> comparator) { this.comparator = comparator; init(events, isSorted); } /** * Creates a new Priority Search Tree for a set of intervals. * * @param intervals a list of the intervals to use in the tree. The intervals are NOT initially stored in the tree. * @param comparator a comparator for elements of the underlying type T. */ public PrioritySearchTree (ArrayList<Interval1D> intervals, Comparator<T> comparator) { this.comparator = comparator; ArrayList<T> obj = new ArrayList<T>(); for (Interval1D interval : intervals) { obj.add((T)(interval.border(false))); obj.add((T)(interval.border(true))); } init(obj, false); } /** * Initializes the sceleton of the priority search tree. * * @param events the event points which may occur in the priority search tree. * @param isSorted flag indicating if <i>events</i> is sorted. */ private void init(ArrayList<T> events, boolean isSorted) { if (!isSorted) Lists.quickSort(events, comparator); // build leaf level ArrayList<Node> leafs = new ArrayList<Node>(); T last = events.get(0); leafs.add(new Node (events.get(0))); for (T event : events) if (!event.equals(last)) leafs.add(new Node (last = event)); // build tree root = buildup(leafs); // the split values are now the maximums of the subtrees // move them one level up for correct split decisions root.leftup(); } /** * Builds up a level of the sceleton. * * @param nodes the nodes to store on the level. * @return root of the tree. */ private Node buildup(ArrayList<Node> nodes) { if (nodes.size()==1) return nodes.get(0); ArrayList<Node> uppernodes = new ArrayList<Node>(); for (int i=0; i<nodes.size(); i+=2) uppernodes.add( (i+1<nodes.size()) ? (new Node (nodes.get(i),nodes.get(i+1),nodes.get(i+1).split)): (nodes.get(i)) ); return buildup(uppernodes); } /** * Inserts an Interval into the tree. * * @param interval the interval to insert. */ public void insert (Interval1D interval) { insert(root, interval); } /** * Recursively inserts an interval inside the tree. * * @param node the node to traverse. * @param interval the interval to insert. */ private void insert (Node node, Interval1D interval) { if (node.interval==null) node.interval = interval; else { if (comparator.compare((T)(node.interval.border(false)), (T)(interval.border(false))) <= 0) { if (comparator.compare(node.split, (T)(interval.border(true))) >= 0) insert(node.left, interval); else insert(node.right, interval); } else { Interval1D tmp = node.interval; node.interval = interval; insert(node, tmp); } } } /** * Removes an interval from the tree. * * @param interval the interval to remove. */ public void remove (Interval1D interval) { remove(root, interval); } /** * Recursively tries to remove an interval from the tree. * * @param node the node to traverse. * @param interval the interval to remove * @return <i>true</i>, if the interval was succesfully removed, <i>false</i> otherwise. */ private boolean remove (Node node, Interval1D interval) { if (node == null || node.interval == null) return false; if ((comparator.compare((T)(node.interval.border(false)),(T)(interval.border(false)))==0) && (comparator.compare((T)(node.interval.border(true)),(T)(interval.border(true)))==0)) { if (node.right==null) { if (node.left==null) node.interval = null; else { node.interval = node.left.interval; remove(node.left, node.left.interval); } } else if (node.right.interval==null || (node.left.interval!=null && comparator.compare((T)(node.left.interval.border(false)),(T)(node.right.interval.border(false)))<0)) { node.interval = node.left.interval; remove(node.left, node.left.interval); } else { node.interval = node.right.interval; remove(node.right, node.right.interval); } return true; } else { if (comparator.compare(node.split,(T)(interval.border(true)))>=0) return remove(node.left, interval); else return remove(node.right, interval); } } /** * Checks if an interval is stored in the tree. * * @param interval the interval to find. * @return <i>true</i>, if interval is contained in the tree, <i>false</i> otherwise */ public boolean contains (Interval1D interval) { return contains(root, interval); } /** * Recursively tries to find an interval in the tree. * * @param node the node to traverse. * @param interval the interval to find * @return <i>true</i>, if the interval was succesfully removed, <i>false</i> otherwise. */ private boolean contains (Node node, Interval1D interval) { if (node == null || node.interval == null) return false; if ((comparator.compare((T)(node.interval.border(false)),(T)(interval.border(false)))==0) && (comparator.compare((T)(node.interval.border(true)),(T)(interval.border(true)))==0)) { return true; } else { if (comparator.compare(node.split,(T)(interval.border(true)))>=0) return remove(node.left,interval); else return remove(node.right,interval); } } /** * Finds all intervals [x,y] for which y lies in the interval yRange and x is less than xMax. * * @param xMax the upper bound for x values (not inclusive) * @param yRange the range for y values (inclusive) * @return all intervals [x,y] with x < xMax and y in yRange */ public ArrayList<Interval1D> query (T xMax, Interval1D yRange) { ArrayList<Interval1D> results = new ArrayList<Interval1D>(); query(root, xMax, yRange, results); return results; } /** * Recursivly finds all intervals [x,y] in the subtree of node for which y lies in the interval yRange and x is less than xMax. * * @param node the root of the subtree to traverse * @param xMax the upper bound for x values (not inclusive) * @param yRange the range for y values (inclusive) * @param results an Arraylist for collecting the results */ private void query (Node node, T xMax, Interval1D yRange, List<Interval1D> results) { if (node==null || node.interval==null) return; if (comparator.compare((T)(node.interval.border(false)), xMax) > 0) return; int cr = yRange.contains(node.split); if (cr==0) results.add(node.interval); if (cr>=0) query (node.right, xMax, yRange, results); if (cr<=0) query (node.left, xMax, yRange, results); } }