java 关于区间树,KD树,线段树,伸展树,后缀树,红黑树的几段代码
区间树
可以统计某个区间对应的重复的区间
package com.jwetherell.algorithms.data_structures; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * An interval tree is an ordered tree data structure to hold intervals. Specifically, it * allows one to efficiently find all intervals that overlap with any given interval or point. * * http://en.wikipedia.org/wiki/Interval_tree * * @author Justin Wetherell <phishman3579@gmail.com> */ public class IntervalTree<O extends Object> { private Interval<O> root = null; private static final Comparator<IntervalData<?>> startComparator = new Comparator<IntervalData<?>>(){ /** * {@inheritDoc} */ @Override public int compare(IntervalData<?> arg0, IntervalData<?> arg1) { if (arg0.start<arg1.start) return -1; if (arg1.start<arg0.start) return 1; return 0; } }; private static final Comparator<IntervalData<?>> endComparator = new Comparator<IntervalData<?>>(){ /** * {@inheritDoc} */ @Override public int compare(IntervalData<?> arg0, IntervalData<?> arg1) { if (arg0.end<arg1.end) return -1; if (arg1.end<arg0.end) return 1; return 0; } }; /** * Create interval tree from list of IntervalData objects; * * @param intervals is a list of IntervalData objects */ public IntervalTree(List<IntervalData<O>> intervals) { if (intervals.size()<=0) return; root = createFromList(intervals); } protected static final <O extends Object> Interval<O> createFromList(List<IntervalData<O>> intervals) { Interval<O> newInterval = new Interval<O>(); int half = intervals.size()/2; IntervalData<O> middle = intervals.get(half); newInterval.center = ((middle.start+middle.end)/2); List<IntervalData<O>> leftIntervals = new ArrayList<IntervalData<O>>(); List<IntervalData<O>> rightIntervals = new ArrayList<IntervalData<O>>(); for (IntervalData<O> interval : intervals) { if (interval.end<newInterval.center) { leftIntervals.add(interval); } else if (interval.start>newInterval.center) { rightIntervals.add(interval); } else { newInterval.overlap.add(interval); } } if (leftIntervals.size()>0) newInterval.left = createFromList(leftIntervals); if (rightIntervals.size()>0) newInterval.right = createFromList(rightIntervals); return newInterval; } /** * Stabbing query * * @param index to query for. * @return data at index. */ public IntervalData<O> query(long index) { return root.query(index); } /** * Range query * * @param start of range to query for. * @param end of range to query for. * @return data for range. */ public IntervalData<O> query(long start, long end) { return root.query(start, end); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(IntervalTreePrinter.getString(this)); return builder.toString(); } protected static class IntervalTreePrinter { public static <O extends Object> String getString(IntervalTree<O> tree) { if (tree.root == null) return "Tree has no nodes."; return getString(tree.root, "", true); } private static <O extends Object> String getString(Interval<O> interval, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); builder.append( prefix + (isTail ? "└── " : "├── ") + interval.toString() + "\n" ); List<Interval<O>> children = new ArrayList<Interval<O>>(); if (interval.left!=null) children.add(interval.left); if (interval.right!=null) children.add(interval.right); if (children.size()>0) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() > 0) { builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } public static final class Interval<O> { private long center = Long.MIN_VALUE; private Interval<O> left = null; private Interval<O> right = null; private Set<IntervalData<O>> overlap = new TreeSet<IntervalData<O>>(startComparator); /** * Stabbing query * * @param index to query for. * @return data at index. */ public IntervalData<O> query(long index) { IntervalData<O> results = null; if (index<center) { //overlap is sorted by start point for (IntervalData<O> data : overlap) { if (data.start>index) break; IntervalData<O> temp = data.query(index); if (results==null && temp!=null) results = temp; else if (temp!=null) results.combined(temp); } } else if (index>=center) { //overlapEnd is sorted by end point Set<IntervalData<O>> overlapEnd = new TreeSet<IntervalData<O>>(endComparator); overlapEnd.addAll(overlap); for (IntervalData<O> data : overlapEnd) { if (data.end<index) break; IntervalData<O> temp = data.query(index); if (results==null && temp!=null) results = temp; else if (temp!=null) results.combined(temp); } } if (index<center) { if (left!=null) { IntervalData<O> temp = left.query(index); if (results==null && temp!=null) results = temp; else if (temp!=null) results.combined(temp); } } else if (index>=center) { if (right!=null) { IntervalData<O> temp = right.query(index); if (results==null && temp!=null) results = temp; else if (temp!=null) results.combined(temp); } } return results; } /** * Range query * * @param start of range to query for. * @param end of range to query for. * @return data for range. */ public IntervalData<O> query(long start, long end) { IntervalData<O> results = null; for (IntervalData<O> data : overlap) { if (data.start > end) break; IntervalData<O> temp = data.query(start,end); if (results==null && temp!=null) results = temp; else if (temp!=null) results.combined(temp); } if (left!=null && start<center) { IntervalData<O> temp = left.query(start,end); if (temp!=null && results==null) results = temp; else if (temp!=null) results.combined(temp); } if (right!=null && end>=center) { IntervalData<O> temp = right.query(start,end); if (temp!=null && results==null) results = temp; else if (temp!=null) results.combined(temp); } return results; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Center=").append(center); builder.append(" Set=").append(overlap); return builder.toString(); } } /** * Data structure representing an interval. */ public static final class IntervalData<O> implements Comparable<IntervalData<O>>{ private long start = Long.MIN_VALUE; private long end = Long.MAX_VALUE; private Set<O> set = new TreeSet<O>(); //Sorted /** * Interval data using O as it's unique identifier * @param object Object which defines the interval data */ public IntervalData(long index, O object) { this.start = index; this.end = index; this.set.add(object); } /** * Interval data using O as it's unique identifier * @param object Object which defines the interval data */ public IntervalData(long start, long end, O object) { this.start = start; this.end = end; this.set.add(object); } /** * Interval data list which should all be unique * @param list of interval data objects */ public IntervalData(long start, long end, Set<O> set) { this.start = start; this.end = end; this.set = set; //Make sure they are unique Iterator<O> iter = set.iterator(); while (iter.hasNext()) { O obj1 = iter.next(); O obj2 = null; if (iter.hasNext()) obj2 = iter.next(); if (obj1.equals(obj2)) throw new InvalidParameterException("Each interval data in the list must be unique."); } } /** * Clear the indices. */ public void clear() { this.start = Long.MIN_VALUE; this.end = Long.MAX_VALUE; this.set.clear(); } /** * Combined this IntervalData with data. * * @param data to combined with. * @return Data which represents the combination. */ public IntervalData<O> combined(IntervalData<O> data) { if (data.start<this.start) this.start = data.start; if (data.end>this.end) this.end = data.end; this.set.addAll(data.set); return this; } /** * Deep copy of data. * * @return deep copy. */ public IntervalData<O> copy() { Set<O> listCopy = new TreeSet<O>(); listCopy.addAll(set); return new IntervalData<O>(start,end,listCopy); } /** * Query inside this data object. * * @param start of range to query for. * @param end of range to query for. * @return Data queried for or NULL if it doesn't match the query. */ public IntervalData<O> query(long index) { if (index<this.start || index>this.end) { //Ignore } else { return copy(); } return null; } /** * Query inside this data object. * * @param start of range to query for. * @param end of range to query for. * @return Data queried for or NULL if it doesn't match the query. */ public IntervalData<O> query(long start, long end) { if (end<this.start || start>this.end) { //Ignore } else { return copy(); } return null; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof IntervalData)) return false; @SuppressWarnings("unchecked") IntervalData<O> data = (IntervalData<O>) obj; if (this.start==data.start && this.end==data.end) { if (this.set.size()!=data.set.size()) return false; for (O o : set) { if (!data.set.contains(o)) return false; } return true; } return false; } /** * {@inheritDoc} */ @Override public int compareTo(IntervalData<O> d) { if (this.end < d.end) return -1; if (d.end < this.end) return 1; return 0; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(start).append("->").append(end); builder.append(" set=").append(set); return builder.toString(); } } }
KD树,统计某个点最近的区间
package com.jwetherell.algorithms.data_structures; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * A k-d tree (short for k-dimensional tree) is a space-partitioning data structure for organizing * points in a k-dimensional space. k-d trees are a useful data structure for several applications, * such as searches involving a multidimensional search key (e.g. range searches and nearest neighbor * searches). k-d trees are a special case of binary space partitioning trees. * * http://en.wikipedia.org/wiki/K-d_tree * * @author Justin Wetherell <phishman3579@gmail.com> */ public class KdTree<T extends KdTree.XYZPoint> { private int k = 3; private KdNode root = null; private static final Comparator<XYZPoint> X_COMPARATOR = new Comparator<XYZPoint>() { /** * {@inheritDoc} */ @Override public int compare(XYZPoint o1, XYZPoint o2) { if (o1.x<o2.x) return -1; if (o1.x>o2.x) return 1; return 0; } }; private static final Comparator<XYZPoint> Y_COMPARATOR = new Comparator<XYZPoint>() { /** * {@inheritDoc} */ @Override public int compare(XYZPoint o1, XYZPoint o2) { if (o1.y<o2.y) return -1; if (o1.y>o2.y) return 1; return 0; } }; private static final Comparator<XYZPoint> Z_COMPARATOR = new Comparator<XYZPoint>() { /** * {@inheritDoc} */ @Override public int compare(XYZPoint o1, XYZPoint o2) { if (o1.z<o2.z) return -1; if (o1.z>o2.z) return 1; return 0; } }; protected static final int X_AXIS = 0; protected static final int Y_AXIS = 1; protected static final int Z_AXIS = 2; /** * Default constructor. */ public KdTree() { } /** * More efficient constructor. * * @param list of XYZPoints. */ public KdTree(List<XYZPoint> list) { root = createNode(list, k, 0); } /** * Create node from list of XYZPoints. * * @param list of XYZPoints. * @param k of the tree. * @param depth depth of the node. * @return node created. */ private static KdNode createNode(List<XYZPoint> list, int k, int depth) { if (list==null || list.size()==0) return null; int axis = depth % k; if (axis==X_AXIS) Collections.sort(list, X_COMPARATOR); else if (axis==Y_AXIS) Collections.sort(list, Y_COMPARATOR); else Collections.sort(list, Z_COMPARATOR); int mediaIndex = list.size()/2; KdNode node = new KdNode(k,depth,list.get(mediaIndex)); if (list.size()>0) { if ((mediaIndex-1)>=0) { List<XYZPoint> less = list.subList(0, mediaIndex); if (less.size()>0) { node.lesser = createNode(less,k,depth+1); node.lesser.parent = node; } } if ((mediaIndex+1)<=(list.size()-1)) { List<XYZPoint> more = list.subList(mediaIndex+1, list.size()); if (more.size()>0) { node.greater = createNode(more,k,depth+1); node.greater.parent = node; } } } return node; } /** * Add value to the tree. Tree can contain multiple equal values. * * @param value T to add to the tree. * @return True if successfully added to tree. */ public boolean add(T value) { if (value==null) return false; if (root==null) { root = new KdNode(value); return true; } KdNode node = root; while (true) { if (KdNode.compareTo(node.depth, node.k, node.id, value)<=0) { //Lesser if (node.lesser==null) { KdNode newNode = new KdNode(k,node.depth+1,value); newNode.parent = node; node.lesser = newNode; break; } else { node = node.lesser; } } else { //Greater if (node.greater==null) { KdNode newNode = new KdNode(k,node.depth+1,value); newNode.parent = node; node.greater = newNode; break; } else { node = node.greater; } } } return true; } /** * Does the tree contain the value. * * @param value T to locate in the tree. * @return True if tree contains value. */ public boolean contains(T value) { if (value==null) return false; KdNode node = getNode(this,value); return (node!=null); } /** * Locate T in the tree. * * @param tree to search. * @param value to search for. * @return KdNode or NULL if not found */ private static final <T extends KdTree.XYZPoint> KdNode getNode(KdTree<T> tree, T value) { if (tree==null || tree.root==null || value==null) return null; KdNode node = tree.root; while (true) { if (node.id.equals(value)) { return node; } else if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) { //Greater if (node.greater==null) { return null; } else { node = node.greater; } } else { //Lesser if (node.lesser==null) { return null; } else { node = node.lesser; } } } } /** * Remove first occurrence of value in the tree. * * @param value T to remove from the tree. * @return True if value was removed from the tree. */ public boolean remove(T value) { if (value==null) return false; KdNode node = getNode(this,value); if (node==null) return false; KdNode parent = node.parent; if (parent!=null) { if (parent.lesser!=null && node.equals(parent.lesser)) { List<XYZPoint> nodes = getTree(node); if (nodes.size()>0) { parent.lesser = createNode(nodes,node.k,node.depth); if (parent.lesser!=null) { parent.lesser.parent = parent; } } else { parent.lesser = null; } } else { List<XYZPoint> nodes = getTree(node); if (nodes.size()>0) { parent.greater = createNode(nodes,node.k,node.depth); if (parent.greater!=null) { parent.greater.parent = parent; } } else { parent.greater = null; } } } else { //root List<XYZPoint> nodes = getTree(node); if (nodes.size()>0) root = createNode(nodes,node.k,node.depth); else root = null; } return true; } /** * Get the (sub) tree rooted at root. * * @param root of tree to get nodes for. * @return points in (sub) tree, not including root. */ private static final List<XYZPoint> getTree(KdNode root) { List<XYZPoint> list = new ArrayList<XYZPoint>(); if (root==null) return list; if (root.lesser!=null) { list.add(root.lesser.id); list.addAll(getTree(root.lesser)); } if (root.greater!=null) { list.add(root.greater.id); list.addAll(getTree(root.greater)); } return list; } /** * K Nearest Neighbor search * * @param K Number of neighbors to retrieve. Can return more than K, if last nodes are equal distances. * @param value to find neighbors of. * @return collection of T neighbors. */ @SuppressWarnings("unchecked") public Collection<T> nearestNeighbourSearch(int K, T value) { if (value==null) return null; //Map used for results TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value)); //Find the closest leaf node KdNode prev = null; KdNode node = root; while (node!=null) { if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) { //Greater prev = node; node = node.greater; } else { //Lesser prev = node; node = node.lesser; } } KdNode leaf = prev; if (leaf!=null) { //Used to not re-examine nodes Set<KdNode> examined = new HashSet<KdNode>(); //Go up the tree, looking for better solutions node = leaf; while (node!=null) { //Search node searchNode(value,node,K,results,examined); node = node.parent; } } //Load up the collection of the results Collection<T> collection = new ArrayList<T>(K); for (KdNode kdNode : results) { collection.add((T)kdNode.id); } return collection; } private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) { examined.add(node); //Search node KdNode lastNode = null; Double lastDistance = Double.MAX_VALUE; if (results.size()>0) { lastNode = results.last(); lastDistance = lastNode.id.euclideanDistance(value); } Double nodeDistance = node.id.euclideanDistance(value); if (nodeDistance.compareTo(lastDistance)<0) { if (results.size()==K && lastNode!=null) results.remove(lastNode); results.add(node); } else if (nodeDistance.equals(lastDistance)) { results.add(node); } else if (results.size()<K) { results.add(node); } lastNode = results.last(); lastDistance = lastNode.id.euclideanDistance(value); int axis = node.depth % node.k; KdNode lesser = node.lesser; KdNode greater = node.greater; //Search children branches, if axis aligned distance is less than current distance if (lesser!=null && !examined.contains(lesser)) { examined.add(lesser); double nodePoint = Double.MIN_VALUE; double valuePlusDistance = Double.MIN_VALUE; if (axis==X_AXIS) { nodePoint = node.id.x; valuePlusDistance = value.x-lastDistance; } else if (axis==Y_AXIS) { nodePoint = node.id.y; valuePlusDistance = value.y-lastDistance; } else { nodePoint = node.id.z; valuePlusDistance = value.z-lastDistance; } boolean lineIntersectsCube = ((valuePlusDistance<=nodePoint)?true:false); //Continue down lesser branch if (lineIntersectsCube) searchNode(value,lesser,K,results,examined); } if (greater!=null && !examined.contains(greater)) { examined.add(greater); double nodePoint = Double.MIN_VALUE; double valuePlusDistance = Double.MIN_VALUE; if (axis==X_AXIS) { nodePoint = node.id.x; valuePlusDistance = value.x+lastDistance; } else if (axis==Y_AXIS) { nodePoint = node.id.y; valuePlusDistance = value.y+lastDistance; } else { nodePoint = node.id.z; valuePlusDistance = value.z+lastDistance; } boolean lineIntersectsCube = ((valuePlusDistance>=nodePoint)?true:false); //Continue down greater branch if (lineIntersectsCube) searchNode(value,greater,K,results,examined); } } /** * {@inheritDoc} */ @Override public String toString() { return TreePrinter.getString(this); } protected static class EuclideanComparator implements Comparator<KdNode> { private XYZPoint point = null; public EuclideanComparator(XYZPoint point) { this.point = point; } /** * {@inheritDoc} */ @Override public int compare(KdNode o1, KdNode o2) { Double d1 = point.euclideanDistance(o1.id); Double d2 = point.euclideanDistance(o2.id); if (d1.compareTo(d2)<0) return -1; else if (d2.compareTo(d1)<0) return 1; return o1.id.compareTo(o2.id); } }; public static class KdNode implements Comparable<KdNode> { private int k = 3; private int depth = 0; private XYZPoint id = null; private KdNode parent = null; private KdNode lesser = null; private KdNode greater = null; public KdNode(XYZPoint id) { this.id = id; } public KdNode(int k, int depth, XYZPoint id) { this(id); this.k = k; this.depth = depth; } public static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2) { int axis = depth % k; if (axis==X_AXIS) return X_COMPARATOR.compare(o1, o2); return Y_COMPARATOR.compare(o1, o2); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (obj==null) return false; if (!(obj instanceof KdNode)) return false; KdNode kdNode = (KdNode) obj; if (this.compareTo(kdNode)==0) return true; return false; } /** * {@inheritDoc} */ @Override public int compareTo(KdNode o) { return compareTo(depth, k, this.id, o.id); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("k=").append(k); builder.append(" depth=").append(depth); builder.append(" id=").append(id.toString()); return builder.toString(); } } public static class XYZPoint implements Comparable<XYZPoint> { private double x = Double.NEGATIVE_INFINITY; private double y = Double.NEGATIVE_INFINITY; private double z = Double.NEGATIVE_INFINITY; public XYZPoint(double x, double y) { this.x = x; this.y = y; this.z = 0; } public XYZPoint(double x, int y, double z) { this.x = x; this.y = y; this.z = z; } /** * Computes the Euclidean distance from this point to the other. * * @param o1 other point. * @return euclidean distance. */ public double euclideanDistance(XYZPoint o1) { return euclideanDistance(o1,this); } /** * Computes the Euclidean distance from one point to the other. * * @param o1 first point. * @param o2 second point. * @return euclidean distance. */ private static final double euclideanDistance(XYZPoint o1, XYZPoint o2) { return Math.sqrt(Math.pow((o1.x-o2.x),2)+Math.pow((o1.y-o2.y),2)+Math.pow((o1.z-o2.z),2)); }; /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (obj == null) return false; if (!(obj instanceof XYZPoint)) return false; XYZPoint xyzPoint = (XYZPoint) obj; int xComp = X_COMPARATOR.compare(this, xyzPoint); if (xComp!=0) return false; int yComp = Y_COMPARATOR.compare(this, xyzPoint); return (yComp==0); } /** * {@inheritDoc} */ @Override public int compareTo(XYZPoint o) { int xComp = X_COMPARATOR.compare(this, o); if (xComp!=0) return xComp; int yComp = Y_COMPARATOR.compare(this, o); return yComp; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("("); builder.append(x).append(", "); builder.append(y).append(", "); builder.append(z); builder.append(")"); return builder.toString(); } } protected static class TreePrinter { public static <T extends XYZPoint> String getString(KdTree<T> tree) { if (tree.root == null) return "Tree has no nodes."; return getString(tree.root, "", true); } private static <T extends Comparable<T>> String getString(KdNode node, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); if (node.parent!=null) { String side = "left"; if (node.parent.greater!=null && node.id.equals(node.parent.greater.id)) side = "right"; builder.append(prefix + (isTail ? "└── " : "├── ") + "[" + side + "] " + "depth=" + node.depth + " id=" + node.id + "\n"); } else { builder.append(prefix + (isTail ? "└── " : "├── ") + "depth=" + node.depth + " id=" + node.id + "\n"); } List<KdNode> children = null; if (node.lesser != null || node.greater != null) { children = new ArrayList<KdNode>(2); if (node.lesser != null) children.add(node.lesser); if (node.greater != null) children.add(node.greater); } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } }
红黑树
package com.jwetherell.algorithms.data_structures; import java.util.ArrayList; import java.util.List; /** * A red–black tree is a type of self-balancing binary search tree, a data structure * used in computer science, typically to implement associative arrays. A red–black tree * is a binary search tree that inserts and deletes in such a way that the tree is always * reasonably balanced. Red-black trees are often compared with AVL trees. AVL trees are * more rigidly balanced, they are faster than red-black trees for lookup intensive * applications. However, red-black trees are faster for insertion and removal. * * http://en.wikipedia.org/wiki/Red%E2%80%93black_tree * * @author Justin Wetherell <phishman3579@gmail.com> */ public class RedBlackTree<T extends Comparable<T>> extends BinarySearchTree<T> implements BinarySearchTree.INodeCreator<T> { protected static final boolean BLACK = false; protected static final boolean RED = true; /** * Default constructor. */ public RedBlackTree() { this.creator = this; } /** * Constructor with external Node creator. */ public RedBlackTree(INodeCreator<T> creator) { super(creator); } /** * {@inheritDoc} */ @Override protected Node<T> addValue(T id) { RedBlackNode<T> nodeAdded = null; boolean added = false; if (root == null) { //Case 1 - The current node is at the root of the tree. if (this.creator==null) { root = new RedBlackNode<T>(null, id, BLACK); root.lesser = new RedBlackNode<T>(root,null,BLACK); root.greater = new RedBlackNode<T>(root,null,BLACK); } else { root = this.creator.createNewNode(null, id); ((RedBlackNode<T>)root).color = BLACK; root.lesser = this.creator.createNewNode(root,null); ((RedBlackNode<T>)root.lesser).color = BLACK; root.greater = this.creator.createNewNode(root,null); ((RedBlackNode<T>)root.greater).color = BLACK; } nodeAdded = (RedBlackNode<T>) root; added = true; } else { //Insert node like a BST would Node<T> node = root; while (node != null) { if (node.id==null) { node.id = id; ((RedBlackNode<T>)node).color = RED; if (this.creator==null) { node.lesser = new RedBlackNode<T>(node,null,BLACK); node.greater = new RedBlackNode<T>(node,null,BLACK); } else { node.lesser = this.creator.createNewNode(node,null); ((RedBlackNode<T>)node.lesser).color = BLACK; node.greater = this.creator.createNewNode(node,null); ((RedBlackNode<T>)node.greater).color = BLACK; } nodeAdded = (RedBlackNode<T>) node; added = true; break; } else if (id.compareTo(node.id) <= 0) { node = node.lesser; } else { node = node.greater; } } } if (added==true) { balanceAfterInsert(nodeAdded); size++; } return nodeAdded; } /** * Post insertion balancing algorithm. * * @param node to begin balancing at. * @return True if balanced. */ private void balanceAfterInsert(RedBlackNode<T> node) { RedBlackNode<T> parent = (RedBlackNode<T>) node.parent; if (parent == null) { //Case 1 - The current node is at the root of the tree. node.color = BLACK; return; } if (parent.color == BLACK) { //Case 2 - The current node's parent is black, so property 4 (both children of every red node are black) is not invalidated. return; } RedBlackNode<T> grandParent = node.getGrandParent(); RedBlackNode<T> uncle = node.getUncle(); if (parent.color==RED && uncle.color==RED) { //Case 3 - If both the parent and the uncle are red, then both of them can be repainted black and the grandparent becomes // red (to maintain property 5 (all paths from any given node to its leaf nodes contain the same number of black nodes)). parent.color=BLACK; uncle.color=BLACK; if (grandParent!=null) { grandParent.color=RED; balanceAfterInsert(grandParent); } } else { if (parent.color==RED && uncle.color==BLACK) { //Case 4 - The parent is red but the uncle is black; also, the current node is the right child of parent, and parent in turn // is the left child of its parent grandparent. if (node.equals(parent.greater) && parent.equals(grandParent.lesser)) { //right-left rotateLeft(parent); node = (RedBlackNode<T>) node.lesser; grandParent = node.getGrandParent(); parent = (RedBlackNode<T>) node.parent; uncle = node.getUncle(); } else if (node.equals(parent.lesser) && parent.equals(grandParent.greater)) { //left-right rotateRight(parent); node = (RedBlackNode<T>) node.greater; grandParent = node.getGrandParent(); parent = (RedBlackNode<T>) node.parent; uncle = node.getUncle(); } } if (parent.color==RED && uncle.color==BLACK) { //Case 5 - The parent is red but the uncle is black, the current node is the left child of parent, and parent is the left child of its parent G. parent.color = BLACK; grandParent.color = RED; if (node.equals(parent.lesser) && parent.equals(grandParent.lesser)) { //left-left rotateRight(grandParent); } else if (node.equals(parent.greater) && parent.equals(grandParent.greater)) { //right-right rotateLeft(grandParent); } } } } /** * {@inheritDoc} */ @Override protected Node<T> removeValue(T value) { RedBlackNode<T> nodeRemoved = (RedBlackNode<T>) super.getNode(value); if (nodeRemoved==null) return null; if (nodeRemoved.isLeaf()) { //No children nodeRemoved.id = null; if (nodeRemoved.parent==null) { root = null; } else { nodeRemoved.id = null; nodeRemoved.color = BLACK; nodeRemoved.lesser = null; nodeRemoved.greater = null; } } else { //At least one child RedBlackNode<T> lesser = (RedBlackNode<T>) nodeRemoved.lesser; RedBlackNode<T> greater = (RedBlackNode<T>) nodeRemoved.greater; if (lesser.id!=null && greater.id!=null) { //Two children RedBlackNode<T> greatestInLesser = (RedBlackNode<T>) this.getGreatest(lesser); if (greatestInLesser==null || greatestInLesser.id==null) greatestInLesser = lesser; //Replace node with greatest in his lesser tree, which leaves us with only one child replaceValueOnly(nodeRemoved,greatestInLesser); nodeRemoved = greatestInLesser; } //Handle one child RedBlackNode<T> child = (RedBlackNode<T>)((nodeRemoved.lesser.id!=null)?nodeRemoved.lesser:nodeRemoved.greater); if (nodeRemoved.color==BLACK) { if (child.color==BLACK) { nodeRemoved.color = RED; } boolean result = balanceAfterDelete(nodeRemoved); if (!result) return null; } replaceWithChild(nodeRemoved,child); if (root.equals(nodeRemoved) && nodeRemoved.isLeaf()) { //If we replaced the root with a leaf, just null out root root = null; } } size--; return nodeRemoved; } /** * Replace value of nodeToReplaceWith with nodeToReplace. * * @param nodeToReplace will get value of nodeToReplaceWith. * @param nodeToReplaceWith will get value NULLed. */ private void replaceValueOnly(RedBlackNode<T> nodeToReplace, RedBlackNode<T> nodeToReplaceWith) { nodeToReplace.id = nodeToReplaceWith.id; nodeToReplaceWith.id = null; } /** * Replace entire contents of nodeToReplace with nodeToReplaceWith. * * @param nodeToReplace will get it's contents replace with nodeToReplaceWith contents. * @param nodeToReplaceWith will not be changed. */ private void replaceWithChild(RedBlackNode<T> nodeToReplace, RedBlackNode<T> nodeToReplaceWith) { nodeToReplace.id = nodeToReplaceWith.id; nodeToReplace.color = nodeToReplaceWith.color; //root should always be black if (nodeToReplace.parent==null) nodeToReplace.color = BLACK; nodeToReplace.lesser = nodeToReplaceWith.lesser; nodeToReplace.greater = nodeToReplaceWith.greater; } /** * Post delete balancing algorithm. * * @param node to begin balancing at. * @return True if balanced or false if error. */ private boolean balanceAfterDelete(RedBlackNode<T> node) { if (node.parent==null) { //Case 1 - node is the new root. return true; } RedBlackNode<T> parent = (RedBlackNode<T>) node.parent; RedBlackNode<T> sibling = node.getSibling(); if (sibling.color==RED) { //Case 2 - sibling is red. parent.color = RED; sibling.color = BLACK; if (node.equals(parent.lesser)) { rotateLeft(parent); //Rotation, need to update parent/sibling parent = (RedBlackNode<T>) node.parent; sibling = node.getSibling(); } else if (node.equals(parent.greater)) { rotateRight(parent); //Rotation, need to update parent/sibling parent = (RedBlackNode<T>) node.parent; sibling = node.getSibling(); } else { System.err.println("Yikes! I'm not related to my parent."); return false; } } if (parent.color==BLACK && sibling.color==BLACK && ((RedBlackNode<T>)sibling.lesser).color==BLACK && ((RedBlackNode<T>)sibling.greater).color==BLACK ) { //Case 3 - parent, sibling, and sibling's children are black. sibling.color = RED; boolean result = balanceAfterDelete(parent); if (!result) return false; } else if (parent.color==RED && sibling.color==BLACK && ((RedBlackNode<T>)sibling.lesser).color==BLACK && ((RedBlackNode<T>)sibling.greater).color==BLACK ) { //Case 4 - sibling and sibling's children are black, but parent is red. sibling.color = RED; parent.color = BLACK; } else { if (sibling.color==BLACK) { //Case 5 - sibling is black, sibling's left child is red, sibling's right child is black, and node is the left child of its parent. if (node.equals(parent.lesser) && ((RedBlackNode<T>)sibling.lesser).color==RED && ((RedBlackNode<T>)sibling.greater).color==BLACK ) { sibling.color = RED; ((RedBlackNode<T>)sibling.lesser).color = RED; rotateRight(sibling); //Rotation, need to update parent/sibling parent = (RedBlackNode<T>) node.parent; sibling = node.getSibling(); } else if (node.equals(parent.greater) && ((RedBlackNode<T>)sibling.lesser).color==BLACK && ((RedBlackNode<T>)sibling.greater).color==RED ) { sibling.color = RED; ((RedBlackNode<T>)sibling.greater).color = RED; rotateLeft(sibling); //Rotation, need to update parent/sibling parent = (RedBlackNode<T>) node.parent; sibling = node.getSibling(); } } //Case 6 - sibling is black, sibling's right child is red, and node is the left child of its parent. sibling.color = parent.color; parent.color = BLACK; if (node.equals(parent.lesser)) { ((RedBlackNode<T>)sibling.greater).color = BLACK; rotateLeft(node.parent); } else if (node.equals(parent.greater)) { ((RedBlackNode<T>)sibling.lesser).color = BLACK; rotateRight(node.parent); } else { System.err.println("Yikes! I'm not related to my parent. "+node.toString()); return false; } } return true; } /** * {@inheritDoc} */ @Override public boolean validate() { if (root==null) return true; if (((RedBlackNode<T>)root).color == RED) { //Root node should be black return false; } return this.validateNode(root); } /** * {@inheritDoc} */ @Override protected boolean validateNode(Node<T> node) { RedBlackNode<T> rbNode = (RedBlackNode<T>) node; RedBlackNode<T> lesser = (RedBlackNode<T>) rbNode.lesser; RedBlackNode<T> greater = (RedBlackNode<T>) rbNode.greater; if (rbNode.isLeaf() && rbNode.color==RED) { //Leafs should not be red return false; } if (rbNode.color==RED) { //You should not have two red nodes in a row if (lesser.color==RED) return false; if (greater.color==RED) return false; } if (!lesser.isLeaf()) { //Check BST property boolean lesserCheck = lesser.id.compareTo(rbNode.id)<=0; if (!lesserCheck) return false; //Check red-black property lesserCheck = this.validateNode(lesser); if (!lesserCheck) return false; } if (!greater.isLeaf()) { //Check BST property boolean greaterCheck = greater.id.compareTo(rbNode.id)>0; if (!greaterCheck) return false; //Check red-black property greaterCheck = this.validateNode(greater); if (!greaterCheck) return false; } return true; } /** * {@inheritDoc} */ @Override public String toString() { return RedBlackTreePrinter.getString(this); } /** * {@inheritDoc} */ @Override public Node<T> createNewNode(Node<T> parent, T id) { return (new RedBlackNode<T>(parent, id, BLACK)); } protected static class RedBlackNode<T extends Comparable<T>> extends Node<T> { protected boolean color = BLACK; protected RedBlackNode(Node<T> parent, T id, boolean color) { super(parent,id); this.color = color; } protected RedBlackNode<T> getGrandParent() { if (parent==null || parent.parent==null) return null; return (RedBlackNode<T>) parent.parent; } protected RedBlackNode<T> getUncle() { RedBlackNode<T> grandParent = getGrandParent(); if (grandParent == null) return null; if (grandParent.lesser!=null && grandParent.lesser.equals(parent)) { return (RedBlackNode<T>) grandParent.greater; } else if (grandParent.greater!=null && grandParent.greater.equals(parent)) { return (RedBlackNode<T>) grandParent.lesser; } return null; } protected RedBlackNode<T> getSibling() { if (parent==null) return null; if (parent.lesser.equals(this)) { return (RedBlackNode<T>) parent.greater; } else if (parent.greater.equals(this)) { return (RedBlackNode<T>) parent.lesser; } else { System.err.println("Yikes! I'm not my parents child."); } return null; } protected boolean isLeaf() { if (lesser!=null) return false; if (greater!=null) return false; return true; } /** * {@inheritDoc} */ @Override public String toString() { return "value=" + id + " color=" + ((color==RED)?"RED":"BLACK") + " isLeaf=" + isLeaf() + " parent=" + ((parent != null) ? parent.id : "NULL") + " lesser=" + ((lesser != null) ? lesser.id : "NULL") + " greater=" + ((greater != null) ? greater.id : "NULL"); } } protected static class RedBlackTreePrinter { public static <T extends Comparable<T>> String getString(RedBlackTree<T> tree) { if (tree.root == null) return "Tree has no nodes."; return getString((RedBlackNode<T>)tree.root, "", true); } public static <T extends Comparable<T>> String getString(RedBlackNode<T> node) { if (node == null) return "Sub-tree has no nodes."; return getString(node, "", true); } private static <T extends Comparable<T>> String getString(RedBlackNode<T> node, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + ((node.color==RED)?"RED":"BLACK") + ") " + node.id + "\n"); List<Node<T>> children = null; if (node.lesser != null || node.greater != null) { children = new ArrayList<Node<T>>(2); if (node.lesser != null) children.add(node.lesser); if (node.greater != null) children.add(node.greater); } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString((RedBlackNode<T>)children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { builder.append(getString((RedBlackNode<T>)children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } }
线段树,可以统计线段的最小值,最大值,和等
package com.jwetherell.algorithms.data_structures; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * Segment tree using objects and pointers. A segment tree is a tree data * structure for storing intervals, or segments. It allows querying which of the * stored segments contain a given point. It is, in principle, a static * structure; that is, its content cannot be modified once the structure is * built. * * http://en.wikipedia.org/wiki/Segment_tree * * This class is meant to be somewhat generic, all you'd have to do is extend the * Data abstract class to store your custom data. I've also included a range minimum, * range maximum, range sum, and interval stabbing implementations. * * @author Justin Wetherell <phishman3579@gmail.com> */ public abstract class SegmentTree<D extends SegmentTree.Data> { protected Segment<D> root = null; /** * Stabbing query * * @param index to query for. * @return data at index. */ public abstract D query(long index); /** * Range query * * @param start of range to query for. * @param end of range to query for. * @return data for range. */ public abstract D query(long start, long end); /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(SegmentTreePrinter.getString(this)); return builder.toString(); } public abstract static class Data implements Comparable<Data> { protected long start = Long.MIN_VALUE; protected long end = Long.MAX_VALUE; /** * Constructor for data at index. * * @param index of data. */ public Data(long index) { this.start = index; this.end = index; } /** * Constructor for data at range. * * @param start of range for data. * @param end of range for data. */ public Data(long start, long end) { this.start = start; this.end = end; } /** * Clear the indices. */ public void clear() { start = Long.MIN_VALUE; end = Long.MAX_VALUE; } /** * Combined this data with data. * * @param data to combined with. * @return Data which represents the combination. */ public abstract Data combined(Data data); /** * Deep copy of data. * * @return deep copy. */ public abstract Data copy(); /** * Query inside this data object. * * @param start of range to query for. * @param end of range to query for. * @return Data queried for or NULL if it doesn't match the query. */ public abstract Data query(long start, long end); /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(start).append("->").append(end); return builder.toString(); } /** * {@inheritDoc} */ @Override public int compareTo(Data d) { if (this.end < d.end) return -1; if (d.end < this.end) return 1; return 0; } /** * Data structure representing points in the x,y space and their location * in the quadrants. */ public static final class QuadrantData extends Data { public long quad1 = 0; public long quad2 = 0; public long quad3 = 0; public long quad4 = 0; public QuadrantData(long index) { super(index); } public QuadrantData(long start, long end) { super(start,end); } public QuadrantData(long index, long quad1, long quad2, long quad3, long quad4) { super(index); this.quad1 = quad1; this.quad2 = quad2; this.quad3 = quad3; this.quad4 = quad4; } /** * {@inheritDoc} */ @Override public void clear() { super.clear(); quad1 = 0; quad2 = 0; quad3 = 0; quad4 = 0; } /** * {@inheritDoc} */ @Override public Data combined(Data data) { QuadrantData q = null; if (data instanceof QuadrantData) { q = (QuadrantData) data; this.combined(q); } return this; } /** * Combined specific to quadrant data. * * @param data to combined. */ private void combined(QuadrantData data) { this.quad1 += data.quad1; this.quad2 += data.quad2; this.quad3 += data.quad3; this.quad4 += data.quad4; } /** * {@inheritDoc} */ @Override public QuadrantData copy() { QuadrantData copy = new QuadrantData(start,end); copy.quad1 = this.quad1; copy.quad2 = this.quad2; copy.quad3 = this.quad3; copy.quad4 = this.quad4; return copy; } /** * {@inheritDoc} */ @Override public Data query(long start, long end) { return copy(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append(quad1).append(","); builder.append(quad2).append(","); builder.append(quad3).append(","); builder.append(quad4); return builder.toString(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof QuadrantData)) return false; QuadrantData data = (QuadrantData) obj; if (this.start==data.start && this.end==data.end && this.quad1==data.quad1 && this.quad2==data.quad2 && this.quad3==data.quad3 && this.quad4==data.quad4) { return true; } return false; } } /** * Data structure representing maximum in the range. */ public static final class RangeMaximumData<N extends Number> extends Data { public N maximum = null; public RangeMaximumData(long index) { super(index); } public RangeMaximumData(long start, long end) { super(start,end); } public RangeMaximumData(long index, N number) { super(index); this.maximum = number; } public RangeMaximumData(long start, long end, N number) { super(start,end); this.maximum = number; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Data combined(Data data) { RangeMaximumData<N> q = null; if (data instanceof RangeMaximumData) { q = (RangeMaximumData<N>) data; this.combined(q); } return this; } /** * Combined for range maximum specific data. * * @param data resulted from the combination. */ private void combined(RangeMaximumData<N> data) { if (this.maximum==null && data.maximum==null) return; else if (this.maximum!=null && data.maximum==null) return; else if (this.maximum==null && data.maximum!=null) this.maximum = data.maximum; else if (data.maximum.doubleValue() > this.maximum.doubleValue()) { this.maximum = data.maximum; } } /** * {@inheritDoc} */ @Override public Data copy() { return new RangeMaximumData<N>(start,end,maximum); } /** * {@inheritDoc} */ @Override public Data query(long start, long end) { return copy(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("maximum=").append(maximum); return builder.toString(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof RangeMaximumData)) return false; @SuppressWarnings("unchecked") RangeMaximumData<N> data = (RangeMaximumData<N>) obj; if (this.start==data.start && this.end==data.end && this.maximum.equals(data.maximum)) return true; return false; } } /** * Data structure representing minimum in the range. */ public static final class RangeMinimumData<N extends Number> extends Data { public N minimum = null; public RangeMinimumData(long index) { super(index); } public RangeMinimumData(long start, long end) { super(start,end); } public RangeMinimumData(long index, N number) { super(index); this.minimum = number; } public RangeMinimumData(long start, long end, N number) { super(start,end); this.minimum = number; } /** * {@inheritDoc} */ @Override public void clear() { super.clear(); minimum = null; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Data combined(Data data) { RangeMinimumData<N> q = null; if (data instanceof RangeMinimumData) { q = (RangeMinimumData<N>) data; this.combined(q); } return this; } /** * Combined specific to range minimum specific data. * * @param data resulted from combination. */ private void combined(RangeMinimumData<N> data) { if (this.minimum==null && data.minimum==null) return; else if (this.minimum!=null && data.minimum==null) return; else if (this.minimum==null && data.minimum!=null) this.minimum = data.minimum; else if (data.minimum.doubleValue() < this.minimum.doubleValue()) { this.minimum = data.minimum; } } /** * {@inheritDoc} */ @Override public Data copy() { return new RangeMinimumData<N>(start,end,minimum); } /** * {@inheritDoc} */ @Override public Data query(long start, long end) { return copy(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("minimum=").append(minimum); return builder.toString(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof RangeMinimumData)) return false; @SuppressWarnings("unchecked") RangeMinimumData<N> data = (RangeMinimumData<N>) obj; if (this.start==data.start && this.end==data.end && this.minimum.equals(data.minimum)) return true; return false; } } /** * Data structure representing sum of the range. */ public static final class RangeSumData<N extends Number> extends Data { public N sum = null; public RangeSumData(long index) { super(index); } public RangeSumData(long start, long end) { super(start,end); } public RangeSumData(long index, N number) { super(index); this.sum = number; } public RangeSumData(long start, long end, N number) { super(start,end); this.sum = number; } /** * {@inheritDoc} */ @Override public void clear() { super.clear(); sum = null; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Data combined(Data data) { RangeSumData<N> q = null; if (data instanceof RangeSumData) { q = (RangeSumData<N>) data; this.combined(q); } return this; } /** * Combined specific to range sum specific data. * * @param data resulted from combination. */ @SuppressWarnings("unchecked") private void combined(RangeSumData<N> data) { if (this.sum==null && data.sum==null) return; else if (this.sum!=null && data.sum==null) return; else if (this.sum==null && data.sum!=null) this.sum = data.sum; else { Double d1 = this.sum.doubleValue(); Double d2 = data.sum.doubleValue(); Double r = d1+d2; this.sum = (N)r; } } /** * {@inheritDoc} */ @Override public Data copy() { return new RangeSumData<N>(start,end,sum); } /** * {@inheritDoc} */ @Override public Data query(long start, long end) { return copy(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("sum=").append(sum); return builder.toString(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof RangeSumData)) return false; @SuppressWarnings("unchecked") RangeSumData<N> data = (RangeSumData<N>) obj; if (this.start==data.start && this.end==data.end && this.sum.equals(data.sum)) return true; return false; } } /** * Data structure representing an interval. */ public static final class IntervalData<O extends Object> extends Data { private Set<O> set = new TreeSet<O>(); //Sorted /** * Interval data using O as it's unique identifier * @param object Object which defines the interval data */ public IntervalData(long index, O object) { super(index); this.set.add(object); } /** * Interval data using O as it's unique identifier * @param object Object which defines the interval data */ public IntervalData(long start, long end, O object) { super(start,end); this.set.add(object); } /** * Interval data list which should all be unique * @param list of interval data objects */ public IntervalData(long start, long end, Set<O> set) { super(start,end); this.set = set; //Make sure they are unique Iterator<O> iter = set.iterator(); while (iter.hasNext()) { O obj1 = iter.next(); O obj2 = null; if (iter.hasNext()) obj2 = iter.next(); if (obj1.equals(obj2)) throw new InvalidParameterException("Each interval data in the list must be unique."); } } /** * {@inheritDoc} */ @Override public void clear() { super.clear(); this.set.clear(); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Data combined(Data data) { IntervalData<O> q = null; if (data instanceof IntervalData) { q = (IntervalData<O>) data; this.combined(q); } return this; } /** * Combined for interval specific data. * * @param data resulted from combination. */ private void combined(IntervalData<O> data) { if (data.start<this.start) this.start = data.start; if (data.end>this.end) this.end = data.end; this.set.addAll(data.set); } /** * {@inheritDoc} */ @Override public Data copy() { Set<O> listCopy = new TreeSet<O>(); listCopy.addAll(set); return new IntervalData<O>(start,end,listCopy); } /** * {@inheritDoc} */ @Override public Data query(long start, long end) { if (end<this.start || start>this.end) { //Ignore } else { return copy(); } return null; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("set=").append(set); return builder.toString(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (!(obj instanceof IntervalData)) return false; @SuppressWarnings("unchecked") IntervalData<O> data = (IntervalData<O>) obj; if (this.start==data.start && this.end==data.end) { if (this.set.size()!=data.set.size()) return false; for (O o : set) { if (!data.set.contains(o)) return false; } return true; } return false; } } } /** * Data structure representing a segment. */ protected abstract static class Segment<D extends Data> implements Comparable<Segment<D>> { protected Segment<D>[] segments = null; protected int length = 0; protected int half = 0; protected long start = 0; protected long end = 0; protected D data = null; protected int minLength = 0; public Segment(int minLength) { this.minLength = minLength; } /** * Query for data in range. * * @param start of the range to query for. * @param end of range to query for. * @return Data in the range. */ public abstract D query(long start, long end); protected boolean hasChildren() { return (segments!=null); } protected Segment<D> getLeftChild() { return segments[0]; } protected Segment<D> getRightChild() { return segments[1]; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(start).append("->"); builder.append(end).append(" "); builder.append("Length=").append(length).append(" "); builder.append("Data={").append(data).append("}"); return builder.toString(); } /** * {@inheritDoc} */ @Override public int compareTo(Segment<D> p) { if (this.end < p.end) return -1; if (p.end < this.end) return 1; return 0; } } protected static class SegmentTreePrinter { public static <D extends SegmentTree.Data> String getString(SegmentTree<D> tree) { if (tree.root == null) return "Tree has no nodes."; return getString(tree.root, "", true); } private static <D extends SegmentTree.Data> String getString(Segment<D> segment, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); builder.append( prefix + (isTail ? "└── " : "├── ") + segment.toString() + "\n" ); List<Segment<D>> children = new ArrayList<Segment<D>>(); if (segment.segments!=null) { for (Segment<D> c : segment.segments) children.add(c); } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() > 1) { builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } /** * Flat segment tree is a variant of segment tree that is designed to store a collection of non-overlapping * segments. This structure is efficient when you need to store values associated with 1 dimensional segments * that never overlap with each other. The end points of stored segments are inclusive, that is, when a * segment spans from 2 to 6, an arbitrary point x within that segment can take a value of 2 <= x <= 6. */ public static final class FlatSegmentTree<D extends Data> extends SegmentTree<D> { public FlatSegmentTree(List<D> data) { this(data,1); } @SuppressWarnings("unchecked") public FlatSegmentTree(List<D> data, int minLength) { if (data.size()<=0) throw new InvalidParameterException("Segments list is empty."); Collections.sort(data); //Make sure they are sorted //Make sure they don't overlap if (data.size()>=2) { for (int i=0; i<(data.size()-2); i++) { Data s1 = data.get(i); Data s2 = data.get(i+1); if (s1.end>s2.start) throw new InvalidParameterException("Segments are overlapping."); } } //Check for gaps List<NonOverlappingSegment<D>> segments = new ArrayList<NonOverlappingSegment<D>>(); for (int i=0; i<data.size(); i++) { if (i<data.size()-1) { Data d1 = data.get(i); NonOverlappingSegment<D> s1 = new NonOverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1); segments.add(s1); Data d2 = data.get(i+1); if (d2.start-d1.end>1) { Data d3 = d1.copy(); d3.clear(); d3.start = d1.end+1; d3.end = d2.start-1; NonOverlappingSegment<D> s3 = new NonOverlappingSegment<D>(minLength,d3.start,d3.end,(D)d3); segments.add(s3); } } else { Data d1 = data.get(i); NonOverlappingSegment<D> s1 = new NonOverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1); segments.add(s1); } } long start = segments.get(0).start; long end = segments.get(segments.size()-1).end; int length = (int)(end-start)+1; root = NonOverlappingSegment.createFromList(minLength,segments,start,length); } /** * {@inheritDoc} */ @Override public D query(long index) { return this.query(index, index); } /** * {@inheritDoc} */ @Override public D query(long start, long end) { if (root==null) return null; if (start<root.start) start = root.start; if (end>root.end) end = root.end; return (D)root.query(start, end); } /** * Data structure representing a non-overlapping segment. */ protected static final class NonOverlappingSegment<D extends Data> extends Segment<D> { private Set<Segment<D>> set = new TreeSet<Segment<D>>(); public NonOverlappingSegment(int minLength) { super(minLength); } public NonOverlappingSegment(int minLength, D data) { this(minLength,data.start,data.end,data); } @SuppressWarnings("unchecked") public NonOverlappingSegment(int minLength,long start, long end, D data) { super(minLength); this.start = start; this.end = end; this.length = ((int)(end-start))+1; if (data==null) return; this.data = ((D)data.copy()); } @SuppressWarnings("unchecked") protected static <D extends Data> Segment<D> createFromList(int minLength, List<NonOverlappingSegment<D>> segments, long start, int length) { NonOverlappingSegment<D> segment = new NonOverlappingSegment<D>(minLength); segment.start = start; segment.end = start+(length-1); segment.length = length; for (Segment<D> s : segments) { if (segment.data==null) segment.data = ((D)s.data.copy()); else segment.data.combined(s.data); //Update our data to reflect all children's data } //If segment is greater or equal to two, split data into children if (segment.length >= 2 && segment.length>=minLength) { segment.half = segment.length / 2; List<NonOverlappingSegment<D>> s1 = new ArrayList<NonOverlappingSegment<D>>(); List<NonOverlappingSegment<D>> s2 = new ArrayList<NonOverlappingSegment<D>>(); for (int i = 0; i < segments.size(); i++) { NonOverlappingSegment<D> s = segments.get(i); long middle = segment.start+segment.half; if (s.end<middle) { s1.add(s); } else if (s.start>=middle) { s2.add(s); } else { //Need to split across middle NonOverlappingSegment<D> ss1 = new NonOverlappingSegment<D>(minLength,s.start,middle-1,s.data); s1.add(ss1); NonOverlappingSegment<D> ss2 = new NonOverlappingSegment<D>(minLength,middle,s.end,s.data); s2.add(ss2); } } Segment<D> sub1 = createFromList(minLength,s1,segment.start,segment.half); Segment<D> sub2 = createFromList(minLength,s2,segment.start+segment.half,segment.length-segment.half); segment.segments = new Segment[] { sub1, sub2 }; } else if (segment.length<=minLength) { for (Segment<D> s : segments) { segment.set.add(s); } } return segment; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public D query(long start, long end) { if (start == this.start && end == this.end) { if (this.data==null) return null; D dataToReturn = ((D)this.data.query(start,end)); return dataToReturn; } else if (!this.hasChildren()) { if (end<this.start || start>this.end) { //Ignore } else { D dataToReturn = null; if (this.set.size()==0) return dataToReturn; for (Segment<D> s : this.set) { if (s.start >= start && s.end <= end) { if (dataToReturn==null) dataToReturn = (D)s.data.query(start,end); else dataToReturn.combined(s.data); } else if (s.start <= start && s.end >= end) { if (dataToReturn==null) dataToReturn = (D)s.data.query(start,end); else dataToReturn.combined(s.data); } } return dataToReturn; } } else if (this.hasChildren()) { if (start <= this.getLeftChild().end && end > this.getLeftChild().end) { Data q1 = this.getLeftChild().query(start, getLeftChild().end); Data q2 = this.getRightChild().query(getRightChild().start, end); if (q1==null && q2==null) return null; if (q1!=null && q2==null) return (D)q1; if (q1==null && q2!=null) return (D)q2; return ((D)q1.combined(q2)); } else if (start <= this.getLeftChild().end && end <= this.getLeftChild().end) { return this.getLeftChild().query(start, end); } return this.getRightChild().query(start, end); } return null; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("Set=").append(set); return builder.toString(); } } } /** * Segment tree is a balanced-binary-tree based data structure efficient for detecting all intervals (or segments) * that contain a given point. The segments may overlap with each other. The end points of stored segments are * inclusive, that is, when an interval spans from 2 to 6, an arbitrary point x within that interval can take a * value of 2 <= x <=6. */ public static final class DynamicSegmentTree<D extends Data> extends SegmentTree<D> { public DynamicSegmentTree(List<D> data) { this(data,1); } @SuppressWarnings("unchecked") public DynamicSegmentTree(List<D> data, int minLength) { if (data.size()<=0) return; //Check for gaps List<OverlappingSegment<D>> segments = new ArrayList<OverlappingSegment<D>>(); for (int i=0; i<data.size(); i++) { if (i<data.size()-1) { Data d1 = data.get(i); OverlappingSegment<D> s1 = new OverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1); segments.add(s1); Data d2 = data.get(i+1); if (d2.start-d1.end>1) { Data d3 = d1.copy(); d3.clear(); d3.start = d1.end+1; d3.end = d2.start-1; OverlappingSegment<D> s3 = new OverlappingSegment<D>(minLength,d3.start,d3.end,(D)d3); segments.add(s3); } } else { Data d1 = data.get(i); OverlappingSegment<D> s1 = new OverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1); segments.add(s1); } } //First start first Collections.sort(segments, new Comparator<OverlappingSegment<D>>(){ @Override public int compare(OverlappingSegment<D> arg0, OverlappingSegment<D> arg1) { if (arg0.start<arg1.start) return -1; if (arg1.start<arg0.start) return 1; return 0; } } ); OverlappingSegment<D> startNode = segments.get(0); long start = startNode.start-1; OverlappingSegment<D> s1 = new OverlappingSegment<D>(minLength,start,startNode.start,null); segments.add(0,s1); //Last end last Collections.sort(segments, new Comparator<OverlappingSegment<D>>(){ @Override public int compare(OverlappingSegment<D> arg0, OverlappingSegment<D> arg1) { if (arg0.end<arg1.end) return -1; if (arg1.end<arg0.end) return 1; return 0; } } ); OverlappingSegment<D> endNode = segments.get(segments.size()-1); long end = endNode.end+1; OverlappingSegment<D> s2 = new OverlappingSegment<D>(minLength,endNode.end,end,null); segments.add(s2); int length = (int)(end-start)+1; root = OverlappingSegment.createFromList(minLength,segments,start,length); } /** * {@inheritDoc} */ @Override public D query(long index) { return this.query(index, index); } /** * {@inheritDoc} */ @Override public D query(long start, long end) { if (root==null) return null; if (start<root.start) start = root.start; if (end>root.end) end = root.end; D result = root.query(start, end); return result; } /** * Data structure representing a possibly overlapping segment. */ protected static final class OverlappingSegment<D extends Data> extends Segment<D> { //Separate range set for fast range queries protected Set<Segment<D>> range = new HashSet<Segment<D>>(); public OverlappingSegment(int minLength) { super(minLength); } @SuppressWarnings("unchecked") public OverlappingSegment(int minLength, long start, long end, D data) { super(minLength); this.start = start; this.end = end; this.length = ((int)(end-start))+1; if (data==null) return; this.data = ((D)data.copy()); } @SuppressWarnings("unchecked") protected static <D extends Data> Segment<D> createFromList(int minLength, List<OverlappingSegment<D>> segments, long start, int length) { OverlappingSegment<D> segment = new OverlappingSegment<D>(minLength); segment.start = start; segment.end = start+(length-1); segment.length = length; for (Segment<D> s : segments) { if (s.data==null) continue; if (s.end<segment.start || s.start>segment.end) { //Ignore } else { segment.range.add(s); } if (s.start==segment.start && s.end==segment.end) { if (segment.data==null) segment.data = ((D)s.data.copy()); else segment.data.combined(s.data); //Update our data to reflect all children's data } else if (!segment.hasChildren() && s.start>=segment.start && s.end<=segment.end) { if (segment.data==null) segment.data = ((D)s.data.copy()); else segment.data.combined(s.data); //Update our data to reflect all children's data } } //If segment is greater or equal to two, split data into children if (segment.length >= 2 && segment.length>=minLength) { segment.half = segment.length / 2; List<OverlappingSegment<D>> s1 = new ArrayList<OverlappingSegment<D>>(); List<OverlappingSegment<D>> s2 = new ArrayList<OverlappingSegment<D>>(); for (int i = 0; i < segments.size(); i++) { OverlappingSegment<D> s = segments.get(i); long middle = segment.start+segment.half; if (s.end<middle) { s1.add(s); } else if (s.start>=middle) { s2.add(s); } else { //Need to split across middle OverlappingSegment<D> ss1 = new OverlappingSegment<D>(minLength,s.start,middle-1,s.data); s1.add(ss1); OverlappingSegment<D> ss2 = new OverlappingSegment<D>(minLength,middle,s.end,s.data); s2.add(ss2); } } Segment<D> sub1 = createFromList(minLength,s1,segment.start,segment.half); Segment<D> sub2 = createFromList(minLength,s2,segment.start+segment.half,segment.length-segment.half); segment.segments = new Segment[] { sub1, sub2 }; } return segment; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public D query(long start, long end) { D result = null; //Use the range data to make range queries faster if (start==this.start && end==this.end) { for (Segment<D> s : this.range) { D temp = (D)s.data.query(start, end); if (temp!=null) { if (result==null) result = (D)temp.copy(); else result.combined(temp); } } } else if (!this.hasChildren()) { if (end<this.start || start>this.end) { //Ignore } else { for (Segment<D> s : this.range) { if (end<s.start || start>s.end) { //Ignore } else { D temp = (D)s.data.query(start, end); if (temp!=null) { if (result==null) result = (D)temp.copy(); else result.combined(temp); } } } } } else { long middle = this.start+this.half; D temp = null; if (start<middle && end>=middle) { temp = this.getLeftChild().query(start, middle-1); D temp2 = this.getRightChild().query(middle, end); if (temp2!=null) { if (temp==null) temp = (D)temp2.copy(); else temp.combined(temp2); } } else if (end<middle) { temp = this.getLeftChild().query(start, end); } else if (start>=middle) { temp = this.getRightChild().query(start, end); } if (temp!=null) result = (D)temp.copy(); } return result; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("Range=").append(range); return builder.toString(); } } } }
伸展树 把最近访问的结点统计旋转变为根结点
package com.jwetherell.algorithms.data_structures; /** * A splay tree is a self-adjusting binary search tree (BST) with the additional * property that recently accessed elements are quick to access again. * * http://en.wikipedia.org/wiki/Splay_tree * * @author Justin Wetherell <phishman3579@gmail.com> */ public class SplayTree<T extends Comparable<T>> extends BinarySearchTree<T> { /** * {@inheritDoc} */ @Override protected Node<T> addValue(T id) { Node<T> nodeToReturn = super.addValue(id); Node<T> nodeAdded = nodeToReturn; if (nodeAdded!=null) { //Splay the new node to the root position while (nodeAdded.parent!=null) { this.splay(nodeAdded); } } return nodeToReturn; } /** * {@inheritDoc} */ @Override protected Node<T> removeValue(T value) { Node<T> nodeToRemove = super.removeValue(value); if (nodeToRemove!=null) { if (nodeToRemove.parent!=null) { Node<T> nodeParent = nodeToRemove.parent; //Splay the parent node to the root position while (nodeParent.parent!=null) { this.splay(nodeParent); } } } return nodeToRemove; } /** * {@inheritDoc} */ @Override public boolean contains(T value) { Node<T> node = getNode(value); if (node!=null) { //Splay the new node to the root position while (node.parent!=null) { this.splay(node); } return true; } return false; } /** * Splay the tree at the node. * @param node to splay at. */ private void splay(Node<T> node) { Node<T> parent = node.parent; Node<T> grandParent = (parent != null) ? parent.parent : null; if (parent == root) { // Zig step root = node; node.parent = null; if (node == parent.lesser) { parent.lesser = node.greater; if (node.greater != null) node.greater.parent = parent; node.greater = parent; parent.parent = node; } else { parent.greater = node.lesser; if (node.lesser != null) node.lesser.parent = parent; node.lesser = parent; parent.parent = node; } } else if (parent != null && grandParent != null) { Node<T> greatGrandParent = grandParent.parent; if (greatGrandParent != null && greatGrandParent.lesser == grandParent) { greatGrandParent.lesser = node; node.parent = greatGrandParent; } else if (greatGrandParent != null && greatGrandParent.greater == grandParent) { greatGrandParent.greater = node; node.parent = greatGrandParent; } else { // I am now root! root = node; node.parent = null; } if ((node == parent.lesser && parent == grandParent.lesser) || (node == parent.greater && parent == grandParent.greater)) { // Zig-zig step if (node == parent.lesser) { Node<T> nodeGreater = node.greater; node.greater = parent; parent.parent = node; parent.lesser = nodeGreater; if (nodeGreater != null) nodeGreater.parent = parent; Node<T> parentGreater = parent.greater; parent.greater = grandParent; grandParent.parent = parent; grandParent.lesser = parentGreater; if (parentGreater != null) parentGreater.parent = grandParent; } else { Node<T> nodeLesser = node.lesser; node.lesser = parent; parent.parent = node; parent.greater = nodeLesser; if (nodeLesser != null) nodeLesser.parent = parent; Node<T> parentLesser = parent.lesser; parent.lesser = grandParent; grandParent.parent = parent; grandParent.greater = parentLesser; if (parentLesser != null) parentLesser.parent = grandParent; } } else { // Zig-zag step if (node == parent.lesser) { Node<T> nodeLesser = node.greater; Node<T> nodeGreater = node.lesser; node.greater = parent; parent.parent = node; node.lesser = grandParent; grandParent.parent = node; parent.lesser = nodeLesser; if (nodeLesser != null) nodeLesser.parent = parent; grandParent.greater = nodeGreater; if (nodeGreater != null) nodeGreater.parent = grandParent; } else { Node<T> nodeLesser = node.lesser; Node<T> nodeGreater = node.greater; node.lesser = parent; parent.parent = node; node.greater = grandParent; grandParent.parent = node; parent.greater = nodeLesser; if (nodeLesser != null) nodeLesser.parent = parent; grandParent.lesser = nodeGreater; if (nodeGreater != null) nodeGreater.parent = grandParent; } } } } }
后缀树
package com.jwetherell.algorithms.data_structures; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; /** * A suffix tree is a data structure that presents the suffixes of a given * string in a way that allows for a particularly fast implementation of many * important string operations. This implementation is based on the Ukkonen's * algorithm. * * http://en.wikipedia.org/wiki/Suffix_tree * * @author Justin Wetherell <phishman3579@gmail.com> */ public class SuffixTree<C extends CharSequence> { private static final char DEFAULT_END_SEQ_CHAR = '$'; private String string = null; private char[] characters = null; private Map<Integer, Link> linksMap = new HashMap<Integer, Link>(); private Map<Integer, Edge<C>> edgeMap = new TreeMap<Integer, Edge<C>>(); private int currentNode = 0; private int firstCharIndex = 0; private int lastCharIndex = -1; private char END_SEQ_CHAR = DEFAULT_END_SEQ_CHAR; /** * Create suffix tree with sequence and default end sequence. * * @param seq to create a suffix tree with. */ public SuffixTree(C seq) { this(seq, DEFAULT_END_SEQ_CHAR); } /** * Create suffix tree with sequence and end sequence parameter. * * @param seq to create a suffix tree with. * @param endSeq which defines the end of a sequence. */ public SuffixTree(C seq, char endSeq) { END_SEQ_CHAR = endSeq; StringBuilder builder = new StringBuilder(seq); if (builder.indexOf(String.valueOf(seq))>=0) builder.append(END_SEQ_CHAR); string = builder.toString(); int length = string.length(); characters = new char[length]; for (int i = 0; i < length; i++) { characters[i] = string.charAt(i); } for (int i = 0; i < length; i++) { addPrefix(i); } } /** * Does the sub-sequence exist in the suffix tree. * * @param sub sub-sequence to locate in the tree. * @return True if the sub-sequence exist in the tree. */ public boolean doesSubStringExist(C sub) { char[] chars = new char[sub.length()]; for (int i = 0; i < sub.length(); i++) { chars[i] = sub.charAt(i); } int[] indices = searchEdges(chars); int start = indices[0]; int end = indices[1]; int length = end - start; if (length == (chars.length - 1)) return true; return false; } /** * Get all the suffixes in the tree. * * @return set of suffixes in the tree. */ public Set<String> getSuffixes() { Set<String> set = getSuffixes(0); return set; } /** * Get all suffixes at starting node. * * @param start node. * @return set of suffixes in the tree at start node. */ private Set<String> getSuffixes(int start) { Set<String> set = new TreeSet<String>(); for (int key : edgeMap.keySet()) { Edge<C> e = edgeMap.get(key); if (e == null) continue; if (e.startNode != start) continue; String s = (string.substring(e.firstCharIndex, e.lastCharIndex + 1)); Link n = linksMap.get(e.endNode); if (n == null) { int index = s.indexOf(END_SEQ_CHAR); if (index>=0) s = s.substring(0, index); set.add(s); } else { Set<String> set2 = getSuffixes(e.endNode); for (String s2 : set2) { int index = s2.indexOf(END_SEQ_CHAR); if (index>=0) s2 = s2.substring(0, index); set.add(s + s2); } } } return set; } /** * Get all edges in the table * * @return debug string. */ public String getEdgesTable() { StringBuilder builder = new StringBuilder(); if (edgeMap.size() > 0) { int lastCharIndex = characters.length; builder.append("Edge\tStart\tEnd\tSuf\tfirst\tlast\tString\n"); for (int key : edgeMap.keySet()) { Edge<C> e = edgeMap.get(key); Link link = linksMap.get(e.endNode); int suffix = (link != null) ? link.suffixNode : -1; builder.append("\t" + e.startNode + "\t" + e.endNode + "\t" + suffix + "\t" + e.firstCharIndex + "\t" + e.lastCharIndex + "\t"); int begin = e.firstCharIndex; int end = (lastCharIndex < e.lastCharIndex) ? lastCharIndex : e.lastCharIndex; builder.append(string.substring(begin, end + 1)); builder.append("\n"); } builder.append("Link\tStart\tEnd\n"); for (int key : linksMap.keySet()) { Link link = linksMap.get(key); builder.append("\t" + link.node + "\t" + link.suffixNode + "\n"); } } return builder.toString(); } /** * Add prefix at index. * * @param index to add prefix at. */ private void addPrefix(int index) { int parentNodeIndex = 0; int lastParentIndex = -1; while (true) { Edge<C> edge = null; parentNodeIndex = currentNode; if (isExplicit()) { edge = Edge.find(this, currentNode, characters[index]); if (edge != null) { // Edge already exists break; } } else { // Implicit node, a little more complicated edge = Edge.find(this, currentNode, characters[firstCharIndex]); int span = lastCharIndex - firstCharIndex; if (characters[edge.firstCharIndex + span + 1] == characters[index]) { // If the edge is the last char, don't split break; } parentNodeIndex = edge.split(currentNode, firstCharIndex, lastCharIndex); } edge = new Edge<C>(this, index, characters.length - 1, parentNodeIndex); if (lastParentIndex > 0) { // Last parent is not root, create a link. linksMap.get(lastParentIndex).suffixNode = parentNodeIndex; } lastParentIndex = parentNodeIndex; if (currentNode == 0) { firstCharIndex++; } else { // Current node is not root, follow link currentNode = linksMap.get(currentNode).suffixNode; } if (!isExplicit()) canonize(); } if (lastParentIndex > 0) { // Last parent is not root, create a link. linksMap.get(lastParentIndex).suffixNode = parentNodeIndex; } lastParentIndex = parentNodeIndex; lastCharIndex++; // Now the endpoint is the next active point if (!isExplicit()) canonize(); }; /** * Is the tree explicit * * @return True if explicit. */ private boolean isExplicit() { return firstCharIndex > lastCharIndex; } /** * Canonize the tree. */ private void canonize() { Edge<C> edge = Edge.find(this, currentNode, characters[firstCharIndex]); int edgeSpan = edge.lastCharIndex - edge.firstCharIndex; while (edgeSpan <= (lastCharIndex - firstCharIndex)) { firstCharIndex = firstCharIndex + edgeSpan + 1; currentNode = edge.endNode; if (firstCharIndex <= lastCharIndex) { edge = Edge.find(this, edge.endNode, characters[firstCharIndex]); edgeSpan = edge.lastCharIndex - edge.firstCharIndex; } } } /** * Returns a two element int array who's 0th index is the start index and * 1th is the end index. */ private int[] searchEdges(char[] query) { int startNode = 0; int queryPosition = 0; int startIndex = -1; int endIndex = -1; boolean stop = false; while (!stop && queryPosition < query.length) { Edge<C> edge = Edge.find(this, startNode, query[queryPosition]); if (edge == null) { stop = true; break; } if (startNode == 0) startIndex = edge.firstCharIndex; for (int i = edge.firstCharIndex; i <= edge.lastCharIndex; i++) { if (queryPosition >= query.length) { stop = true; break; } else if (query[queryPosition] == characters[i]) { queryPosition++; endIndex = i; } else { stop = true; break; } } if (!stop) { // proceed with next node startNode = edge.endNode; if (startNode == -1) stop = true; } } return (new int[] { startIndex, endIndex }); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("String = ").append(this.string).append("\n"); builder.append("End of word character = ").append(END_SEQ_CHAR).append("\n"); builder.append(TreePrinter.getString(this)); return builder.toString(); } private static class Link implements Comparable<Link> { private int node = 0; private int suffixNode = -1; public Link(int node) { this.node = node; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("node=").append(node).append("\n"); builder.append("suffixNode=").append(suffixNode).append("\n"); return builder.toString(); } /** * {@inheritDoc} */ @Override public int compareTo(Link link) { if (link == null) return -1; if (node < link.node) return -1; if (node > link.node) return 1; if (suffixNode < link.suffixNode) return -1; if (suffixNode > link.suffixNode) return 1; return 0; } }; private static class Edge<C extends CharSequence> implements Comparable<Edge<C>> { private static final int KEY_MOD = 2179; // Should be a prime that is // roughly 10% larger than the // String private static int count = 1; private SuffixTree<C> tree = null; private int startNode = -1; private int endNode = 0; private int firstCharIndex = 0; private int lastCharIndex = 0; private Edge(SuffixTree<C> tree, int first, int last, int parent) { this.tree = tree; firstCharIndex = first; lastCharIndex = last; startNode = parent; endNode = count++; insert(this); } private int getKey() { return key(startNode, tree.characters[firstCharIndex]); } private static int key(int node, char c) { return ((node << 8) + c) % KEY_MOD; } private void insert(Edge<C> edge) { tree.edgeMap.put(edge.getKey(), edge); } private void remove(Edge<C> edge) { int i = edge.getKey(); Edge<C> e = tree.edgeMap.remove(i); while (true) { e.startNode = -1; int j = i; while (true) { i = ++i % KEY_MOD; e = tree.edgeMap.get(i); if (e == null) return; int r = key(e.startNode, tree.characters[e.firstCharIndex]); if (i >= r && r > j) continue; if (r > j && j > i) continue; if (j > i && i >= r) continue; break; } tree.edgeMap.put(j, e); } } private static <C extends CharSequence> Edge<C> find(SuffixTree<C> tree, int node, char c) { int key = key(node, c); return tree.edgeMap.get(key); } private int split(int originNode, int firstCharIndex, int lastCharIndex) { remove(this); Edge<C> newEdge = new Edge<C>(tree, this.firstCharIndex, this.firstCharIndex + lastCharIndex - firstCharIndex, originNode); Link link = tree.linksMap.get(newEdge.endNode); if (link == null) { link = new Link(newEdge.endNode); tree.linksMap.put(newEdge.endNode, link); } tree.linksMap.get(newEdge.endNode).suffixNode = originNode; this.firstCharIndex += lastCharIndex - firstCharIndex + 1; this.startNode = newEdge.endNode; insert(this); return newEdge.endNode; } /** * {@inheritDoc} */ @Override public int hashCode() { return getKey(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj instanceof Edge) return false; @SuppressWarnings("unchecked") Edge<C> e = (Edge<C>) obj; if (startNode == e.startNode && tree.characters[firstCharIndex] == tree.characters[e.firstCharIndex]) { return true; } return false; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("startNode=").append(startNode).append("\n"); builder.append("endNode=").append(endNode).append("\n"); builder.append("firstCharIndex=").append(firstCharIndex).append("\n"); builder.append("lastCharIndex=").append(lastCharIndex).append("\n"); String s = tree.string.substring(firstCharIndex, lastCharIndex + 1); builder.append("string=").append(s).append("\n"); return builder.toString(); } /** * {@inheritDoc} */ @Override public int compareTo(Edge<C> edge) { if (edge == null) return -1; if (startNode < edge.startNode) return -1; if (startNode > edge.startNode) return 1; if (endNode < edge.endNode) return -1; if (endNode > edge.endNode) return 1; if (firstCharIndex < edge.firstCharIndex) return -1; if (firstCharIndex > edge.firstCharIndex) return 1; if (lastCharIndex < edge.lastCharIndex) return -1; if (lastCharIndex > edge.lastCharIndex) return 1; return 0; } } protected static class TreePrinter { public static <C extends CharSequence> void printNode(SuffixTree<C> tree) { System.out.println(getString(tree, null, "", true)); } public static <C extends CharSequence> String getString(SuffixTree<C> tree) { return getString(tree, null, "", true); } private static <C extends CharSequence> String getString(SuffixTree<C> tree, Edge<C> e, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); int value = 0; if (e != null) { value = e.endNode; String string = tree.string.substring(e.firstCharIndex, e.lastCharIndex + 1); int index = string.indexOf(tree.END_SEQ_CHAR); if (index>=0) string = string.substring(0, index+1); builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + value + ") " + string + "\n"); } else { builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + 0 + ")" + "\n"); } if (tree.edgeMap.size() > 0) { List<Edge<C>> children = new LinkedList<Edge<C>>(); for (Edge<C> edge : tree.edgeMap.values()) { if (edge != null && (edge.startNode == value)) { children.add(edge); } } if (children.size()>0) { for (int i = 0; i < children.size() - 1; i++) { Edge<C> edge = children.get(i); builder.append(getString(tree, edge, prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { Edge<C> edge = children.get(children.size() - 1); builder.append(getString(tree, edge, prefix + (isTail ? " " : "│ "), true)); } } } return builder.toString(); } } }
测试代码
private static boolean testIntervalTree() { { //Interval tree if (debug>1) System.out.println("Interval Tree."); java.util.List<IntervalTree.IntervalData<String>> intervals = new ArrayList<IntervalTree.IntervalData<String>>(); intervals.add((new IntervalTree.IntervalData<String>(2, 6, "RED"))); intervals.add((new IntervalTree.IntervalData<String>(3, 5, "ORANGE"))); intervals.add((new IntervalTree.IntervalData<String>(4, 11, "GREEN"))); intervals.add((new IntervalTree.IntervalData<String>(5, 10, "DARK_GREEN"))); intervals.add((new IntervalTree.IntervalData<String>(8, 12, "BLUE"))); intervals.add((new IntervalTree.IntervalData<String>(9, 14, "PURPLE"))); intervals.add((new IntervalTree.IntervalData<String>(13, 15, "BLACK"))); IntervalTree<String> tree = new IntervalTree<String>(intervals); if (debug>1) System.out.println(tree); IntervalTree.IntervalData<String> query = tree.query(2); if (debug>1) System.out.println("2: "+query); query = tree.query(4); //Stabbing query if (debug>1) System.out.println("4: "+query); query = tree.query(9); //Stabbing query if (debug>1) System.out.println("9: "+query); query = tree.query(1, 16); //Range query if (debug>1) System.out.println("1->16: "+query); query = tree.query(7, 14); //Range query if (debug>1) System.out.println("7->14: "+query); query = tree.query(14, 15); //Range query if (debug>1) System.out.println("14->15: "+query); if (debug>1) System.out.println(); } { //Lifespan Interval tree if (debug>1) System.out.println("Lifespan Interval Tree."); java.util.List<IntervalTree.IntervalData<String>> intervals = new ArrayList<IntervalTree.IntervalData<String>>(); intervals.add((new IntervalTree.IntervalData<String>(1888, 1971, "Stravinsky"))); intervals.add((new IntervalTree.IntervalData<String>(1874, 1951, "Schoenberg"))); intervals.add((new IntervalTree.IntervalData<String>(1843, 1907, "Grieg"))); intervals.add((new IntervalTree.IntervalData<String>(1779, 1828, "Schubert"))); intervals.add((new IntervalTree.IntervalData<String>(1756, 1791, "Mozart"))); intervals.add((new IntervalTree.IntervalData<String>(1585, 1672, "Schuetz"))); IntervalTree<String> tree = new IntervalTree<String>(intervals); if (debug>1) System.out.println(tree); IntervalTree.IntervalData<String> query = tree.query(1890); if (debug>1) System.out.println("1890: "+query); query = tree.query(1909); //Stabbing query if (debug>1) System.out.println("1909: "+query); query = tree.query(1792, 1903); //Range query if (debug>1) System.out.println("1792->1903: "+query); query = tree.query(1776, 1799); //Range query if (debug>1) System.out.println("1776->1799: "+query); if (debug>1) System.out.println(); } return true; } private static boolean testJavaRedBlackTree() { { long count = 0; long addTime = 0L; long removeTime = 0L; long beforeAddTime = 0L; long afterAddTime = 0L; long beforeRemoveTime = 0L; long afterRemoveTime = 0L; long memory = 0L; long beforeMemory = 0L; long afterMemory = 0L; //Java's Red-Black Tree if (debug>1) System.out.println("Java's Red-Black Tree"); testNames[testIndex] = "Java's RedBlack Tree"; count++; if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); if (debugTime) beforeAddTime = System.currentTimeMillis(); java.util.TreeSet<Integer> tree = new java.util.TreeSet<Integer>(); for (int i=0; i<unsorted.length; i++) { int item = unsorted[i]; tree.add(item); } if (debugTime) { afterAddTime = System.currentTimeMillis(); addTime += afterAddTime-beforeAddTime; if (debug>0) System.out.println("Java's Red-Black Tree add time = "+addTime/count+" ms"); } if (debugMemory) { afterMemory = DataStructures.getMemoryUse(); memory += afterMemory-beforeMemory; if (debug>0) System.out.println("Java's Red-Black Tree memory use = "+(memory/count)+" bytes"); } boolean contains = tree.contains(INVALID); boolean removed = tree.remove(INVALID); if (contains || removed) { System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); if (debug>1) System.out.println(tree.toString()); long lookupTime = 0L; long beforeLookupTime = 0L; long afterLookupTime = 0L; if (debugTime) beforeLookupTime = System.currentTimeMillis(); for (int item : unsorted) { tree.contains(item); } if (debugTime) { afterLookupTime = System.currentTimeMillis(); lookupTime += afterLookupTime-beforeLookupTime; if (debug>0) System.out.println("Java's Red-Black lookup time = "+lookupTime/count+" ms"); } if (debugTime) beforeRemoveTime = System.currentTimeMillis(); for (int i=0; i<unsorted.length; i++) { int item = unsorted[i]; tree.remove(item); } if (debugTime) { afterRemoveTime = System.currentTimeMillis(); removeTime += afterRemoveTime-beforeRemoveTime; if (debug>0) System.out.println("Java's Red-Black Tree remove time = "+removeTime/count+" ms"); } contains = tree.contains(INVALID); removed = tree.remove(INVALID); if (contains || removed) { System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); count++; if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); if (debugTime) beforeAddTime = System.currentTimeMillis(); for (int i=unsorted.length-1; i>=0; i--) { int item = unsorted[i]; tree.add(item); } if (debugTime) { afterAddTime = System.currentTimeMillis(); addTime += afterAddTime-beforeAddTime; if (debug>0) System.out.println("Java's Red-Black Tree add time = "+addTime/count+" ms"); } if (debugMemory) { afterMemory = DataStructures.getMemoryUse(); memory += afterMemory-beforeMemory; if (debug>0) System.out.println("Java's Red-Black Tree memory use = "+(memory/count)+" bytes"); } contains = tree.contains(INVALID); removed = tree.remove(INVALID); if (contains || removed) { System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); if (debug>1) System.out.println(tree.toString()); lookupTime = 0L; beforeLookupTime = 0L; afterLookupTime = 0L; if (debugTime) beforeLookupTime = System.currentTimeMillis(); for (int item : unsorted) { tree.contains(item); } if (debugTime) { afterLookupTime = System.currentTimeMillis(); lookupTime += afterLookupTime-beforeLookupTime; if (debug>0) System.out.println("Java's Red-Black Tree lookup time = "+lookupTime/count+" ms"); } if (debugTime) beforeRemoveTime = System.currentTimeMillis(); for (int i=0; i<unsorted.length; i++) { int item = unsorted[i]; tree.remove(item); } if (debugTime) { afterRemoveTime = System.currentTimeMillis(); removeTime += afterRemoveTime-beforeRemoveTime; if (debug>0) System.out.println("Java's Red-Black Tree remove time = "+removeTime/count+" ms"); } contains = tree.contains(INVALID); removed = tree.remove(INVALID); if (contains || removed) { System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); //sorted long addSortedTime = 0L; long removeSortedTime = 0L; long beforeAddSortedTime = 0L; long afterAddSortedTime = 0L; long beforeRemoveSortedTime = 0L; long afterRemoveSortedTime = 0L; if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); if (debugTime) beforeAddSortedTime = System.currentTimeMillis(); for (int i=0; i<sorted.length; i++) { int item = sorted[i]; tree.add(item); } if (debugTime) { afterAddSortedTime = System.currentTimeMillis(); addSortedTime += afterAddSortedTime-beforeAddSortedTime; if (debug>0) System.out.println("Java's Red-Black Tree add time = "+addSortedTime+" ms"); } if (debugMemory) { afterMemory = DataStructures.getMemoryUse(); memory += afterMemory-beforeMemory; if (debug>0) System.out.println("Java's Red-Black Tree memory use = "+(memory/(count+1))+" bytes"); } contains = tree.contains(INVALID); removed = tree.remove(INVALID); if (contains || removed) { System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); if (debug>1) System.out.println(tree.toString()); lookupTime = 0L; beforeLookupTime = 0L; afterLookupTime = 0L; if (debugTime) beforeLookupTime = System.currentTimeMillis(); for (int item : sorted) { tree.contains(item); } if (debugTime) { afterLookupTime = System.currentTimeMillis(); lookupTime += afterLookupTime-beforeLookupTime; if (debug>0) System.out.println("Java's Red-Black Tree lookup time = "+lookupTime/(count+1)+" ms"); } if (debugTime) beforeRemoveSortedTime = System.currentTimeMillis(); for (int i=sorted.length-1; i>=0; i--) { int item = sorted[i]; tree.remove(item); } if (debugTime) { afterRemoveSortedTime = System.currentTimeMillis(); removeSortedTime += afterRemoveSortedTime-beforeRemoveSortedTime; if (debug>0) System.out.println("Java's Red-Black Tree remove time = "+removeSortedTime+" ms"); } contains = tree.contains(INVALID); removed = tree.remove(INVALID); if (contains || removed) { System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed); if (testResults[testIndex]==null) testResults[testIndex] = new long[6]; testResults[testIndex][0]+=addTime/count; testResults[testIndex][1]+=removeTime/count; testResults[testIndex][2]+=addSortedTime; testResults[testIndex][3]+=removeSortedTime; testResults[testIndex][4]+=lookupTime/(count+1); testResults[testIndex++][5]+=memory/(count+1); if (debug>1) System.out.println(); } return true; } private static boolean testKdTree() { { // K-D TREE if (debug>1) System.out.println("k-d tree with node."); java.util.List<KdTree.XYZPoint> points = new ArrayList<KdTree.XYZPoint>(); KdTree.XYZPoint p1 = new KdTree.XYZPoint(2,3); points.add(p1); KdTree.XYZPoint p2 = new KdTree.XYZPoint(5,4); points.add(p2); KdTree.XYZPoint p3 = new KdTree.XYZPoint(9,6); points.add(p3); KdTree.XYZPoint p4 = new KdTree.XYZPoint(4,7); points.add(p4); KdTree.XYZPoint p5 = new KdTree.XYZPoint(8,1); points.add(p5); KdTree.XYZPoint p6 = new KdTree.XYZPoint(7,2); points.add(p6); KdTree<KdTree.XYZPoint> kdTree = new KdTree<KdTree.XYZPoint>(points); if (debug>1) System.out.println(kdTree.toString()); Collection<KdTree.XYZPoint> result = kdTree.nearestNeighbourSearch(1,p3); if (debug>1) System.out.println("NNS for "+p3+" result="+result+"\n"); KdTree.XYZPoint search = new KdTree.XYZPoint(1,4); result = kdTree.nearestNeighbourSearch(4,search); if (debug>1) System.out.println("NNS for "+search+" result="+result+"\n"); kdTree.remove(p6); if (debug>1) System.out.println("Removed "+p6+"\n"+kdTree.toString()); kdTree.remove(p4); if (debug>1) System.out.println("Removed "+p4+"\n"+kdTree.toString()); kdTree.remove(p3); if (debug>1) System.out.println("Removed "+p3+"\n"+kdTree.toString()); kdTree.remove(p5); if (debug>1) System.out.println("Removed "+p5+"\n"+kdTree.toString()); kdTree.remove(p1); if (debug>1) System.out.println("Removed "+p1+"\n"+kdTree.toString()); kdTree.remove(p2); if (debug>1) System.out.println("Removed "+p2+"\n"+kdTree.toString()); if (debug>1) System.out.println(); } return true; } private static boolean testSegmentTree() { { //Quadrant Segment tree if (debug>1) System.out.println("Quadrant Segment Tree."); java.util.List<SegmentTree.Data.QuadrantData> segments = new ArrayList<SegmentTree.Data.QuadrantData>(); segments.add(new SegmentTree.Data.QuadrantData(0, 1, 0, 0, 0)); //first point in the 0th quadrant segments.add(new SegmentTree.Data.QuadrantData(1, 0, 1, 0, 0)); //second point in the 1st quadrant segments.add(new SegmentTree.Data.QuadrantData(2, 0, 0, 1, 0)); //third point in the 2nd quadrant segments.add(new SegmentTree.Data.QuadrantData(3, 0, 0, 0, 1)); //fourth point in the 3rd quadrant FlatSegmentTree<SegmentTree.Data.QuadrantData> tree = new FlatSegmentTree<SegmentTree.Data.QuadrantData>(segments); if (debug>1) System.out.println(tree); SegmentTree.Data.QuadrantData query = tree.query(0, 3); if (debug>1) System.out.println("0->3: "+query+"\n"); query = tree.query(2, 3); if (debug>1) System.out.println("2->3: "+query+"\n"); query = tree.query(0, 2); if (debug>1) System.out.println("0->2: "+query+"\n"); if (debug>1) System.out.println(); } { //Range Maximum Segment tree if (debug>1) System.out.println("Range Maximum Segment Tree."); java.util.List<SegmentTree.Data.RangeMaximumData<Integer>> segments = new ArrayList<SegmentTree.Data.RangeMaximumData<Integer>>(); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(0, (Integer)4)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(1, (Integer)2)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(2, (Integer)6)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(3, (Integer)3)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(4, (Integer)1)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(5, (Integer)5)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(6, (Integer)0)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(7, 17, (Integer)7)); segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(21, (Integer)10)); FlatSegmentTree<SegmentTree.Data.RangeMaximumData<Integer>> tree = new FlatSegmentTree<SegmentTree.Data.RangeMaximumData<Integer>>(segments,3); if (debug>1) System.out.println(tree); SegmentTree.Data.RangeMaximumData<Integer> query = tree.query(0, 7); if (debug>1) System.out.println("0->7: "+query+"\n"); query = tree.query(0, 21); if (debug>1) System.out.println("0->21: "+query+"\n"); query = tree.query(2, 5); if (debug>1) System.out.println("2->5: "+query+"\n"); query = tree.query(7); if (debug>1) System.out.println("7: "+query+"\n"); if (debug>1) System.out.println(); } { //Range Minimum Segment tree if (debug>1) System.out.println("Range Minimum Segment Tree."); java.util.List<SegmentTree.Data.RangeMinimumData<Integer>> segments = new ArrayList<SegmentTree.Data.RangeMinimumData<Integer>>(); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(0, (Integer)4)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(1, (Integer)2)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(2, (Integer)6)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(3, (Integer)3)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(4, (Integer)1)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(5, (Integer)5)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(6, (Integer)0)); segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(17, (Integer)7)); FlatSegmentTree<SegmentTree.Data.RangeMinimumData<Integer>> tree = new FlatSegmentTree<SegmentTree.Data.RangeMinimumData<Integer>>(segments,5); if (debug>1) System.out.println(tree); SegmentTree.Data.RangeMinimumData<Integer> query = tree.query(0, 7); if (debug>1) System.out.println("0->7: "+query+"\n"); query = tree.query(0, 17); if (debug>1) System.out.println("0->17: "+query+"\n"); query = tree.query(1, 3); if (debug>1) System.out.println("1->3: "+query+"\n"); query = tree.query(7); if (debug>1) System.out.println("7: "+query+"\n"); if (debug>1) System.out.println(); } { //Range Sum Segment tree if (debug>1) System.out.println("Range Sum Segment Tree."); java.util.List<SegmentTree.Data.RangeSumData<Integer>> segments = new ArrayList<SegmentTree.Data.RangeSumData<Integer>>(); segments.add(new SegmentTree.Data.RangeSumData<Integer>(0, (Integer)4)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(1, (Integer)2)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(2, (Integer)6)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(3, (Integer)3)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(4, (Integer)1)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(5, (Integer)5)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(6, (Integer)0)); segments.add(new SegmentTree.Data.RangeSumData<Integer>(17, (Integer)7)); FlatSegmentTree<SegmentTree.Data.RangeSumData<Integer>> tree = new FlatSegmentTree<SegmentTree.Data.RangeSumData<Integer>>(segments,10); if (debug>1) System.out.println(tree); SegmentTree.Data.RangeSumData<Integer> query = tree.query(0, 8); if (debug>1) System.out.println("0->8: "+query+"\n"); query = tree.query(0, 17); if (debug>1) System.out.println("0->17: "+query+"\n"); query = tree.query(2, 5); if (debug>1) System.out.println("2->5: "+query+"\n"); query = tree.query(10, 17); if (debug>1) System.out.println("10->17: "+query+"\n"); query = tree.query(16); if (debug>1) System.out.println("16: "+query+"\n"); query = tree.query(17); if (debug>1) System.out.println("17: "+query+"\n"); if (debug>1) System.out.println(); } { //Interval Segment tree if (debug>1) System.out.println("Interval Segment Tree."); java.util.List<SegmentTree.Data.IntervalData<String>> segments = new ArrayList<SegmentTree.Data.IntervalData<String>>(); segments.add((new SegmentTree.Data.IntervalData<String>(2, 6, "RED"))); segments.add((new SegmentTree.Data.IntervalData<String>(3, 5, "ORANGE"))); segments.add((new SegmentTree.Data.IntervalData<String>(4, 11, "GREEN"))); segments.add((new SegmentTree.Data.IntervalData<String>(5, 10, "DARK_GREEN"))); segments.add((new SegmentTree.Data.IntervalData<String>(8, 12, "BLUE"))); segments.add((new SegmentTree.Data.IntervalData<String>(9, 14, "PURPLE"))); segments.add((new SegmentTree.Data.IntervalData<String>(13, 15, "BLACK"))); DynamicSegmentTree<SegmentTree.Data.IntervalData<String>> tree = new DynamicSegmentTree<SegmentTree.Data.IntervalData<String>>(segments); if (debug>1) System.out.println(tree); SegmentTree.Data.IntervalData<String> query = tree.query(2); if (debug>1) System.out.println("2: "+query); query = tree.query(4); //Stabbing query if (debug>1) System.out.println("4: "+query); query = tree.query(9); //Stabbing query if (debug>1) System.out.println("9: "+query); query = tree.query(1, 16); //Range query if (debug>1) System.out.println("1->16: "+query); query = tree.query(7, 14); //Range query if (debug>1) System.out.println("7->14: "+query); query = tree.query(14, 15); //Range query if (debug>1) System.out.println("14->15: "+query); if (debug>1) System.out.println(); } { //Lifespan Interval Segment tree if (debug>1) System.out.println("Lifespan Interval Segment Tree."); java.util.List<SegmentTree.Data.IntervalData<String>> segments = new ArrayList<SegmentTree.Data.IntervalData<String>>(); segments.add((new SegmentTree.Data.IntervalData<String>(1888, 1971, "Stravinsky"))); segments.add((new SegmentTree.Data.IntervalData<String>(1874, 1951, "Schoenberg"))); segments.add((new SegmentTree.Data.IntervalData<String>(1843, 1907, "Grieg"))); segments.add((new SegmentTree.Data.IntervalData<String>(1779, 1828, "Schubert"))); segments.add((new SegmentTree.Data.IntervalData<String>(1756, 1791, "Mozart"))); segments.add((new SegmentTree.Data.IntervalData<String>(1585, 1672, "Schuetz"))); DynamicSegmentTree<SegmentTree.Data.IntervalData<String>> tree = new DynamicSegmentTree<SegmentTree.Data.IntervalData<String>>(segments,25); if (debug>1) System.out.println(tree); SegmentTree.Data.IntervalData<String> query = tree.query(1890); if (debug>1) System.out.println("1890: "+query); query = tree.query(1909); //Stabbing query if (debug>1) System.out.println("1909: "+query); query = tree.query(1792, 1903); //Range query if (debug>1) System.out.println("1792->1903: "+query); query = tree.query(1776, 1799); //Range query if (debug>1) System.out.println("1776->1799: "+query); if (debug>1) System.out.println(); } return true; } private static boolean testSplayTree() { { long count = 0; long addTime = 0L; long removeTime = 0L; long beforeAddTime = 0L; long afterAddTime = 0L; long beforeRemoveTime = 0L; long afterRemoveTime = 0L; long memory = 0L; long beforeMemory = 0L; long afterMemory = 0L; //Splay Tree if (debug>1) System.out.println("Splay Tree."); testNames[testIndex] = "Splay Tree"; count++; if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); if (debugTime) beforeAddTime = System.currentTimeMillis(); SplayTree<Integer> splay = new SplayTree<Integer>(); for (int i=0; i<unsorted.length; i++) { int item = unsorted[i]; splay.add(item); if (validateStructure && !(splay.size()==(i+1))) { System.err.println("YIKES!! "+item+" caused a size mismatch."); handleError(splay); return false; } if (validateContents && !splay.contains(item)) { System.err.println("YIKES!! "+item+" doesn't exists."); handleError(splay); return false; } } if (debugTime) { afterAddTime = System.currentTimeMillis(); addTime += afterAddTime-beforeAddTime; if (debug>0) System.out.println("Splay Tree add time = "+addTime/count+" ms"); } if (debugMemory) { afterMemory = DataStructures.getMemoryUse(); memory += afterMemory-beforeMemory; if (debug>0) System.out.println("Splay Tree memory use = "+(memory/count)+" bytes"); } boolean contains = splay.contains(INVALID); boolean removed = splay.remove(INVALID); if (contains || removed) { System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); if (debug>1) System.out.println(splay.toString()); long lookupTime = 0L; long beforeLookupTime = 0L; long afterLookupTime = 0L; if (debugTime) beforeLookupTime = System.currentTimeMillis(); for (int item : unsorted) { splay.contains(item); } if (debugTime) { afterLookupTime = System.currentTimeMillis(); lookupTime += afterLookupTime-beforeLookupTime; if (debug>0) System.out.println("Splay Tree lookup time = "+lookupTime/count+" ms"); } if (debugTime) beforeRemoveTime = System.currentTimeMillis(); for (int i=0; i<unsorted.length; i++) { int item = unsorted[i]; splay.remove(item); if (validateStructure && !(splay.size()==((unsorted.length-1)-i))) { System.err.println("YIKES!! "+item+" caused a size mismatch."); handleError(splay); return false; } if (validateContents && splay.contains(item)) { System.err.println("YIKES!! "+item+" still exists."); handleError(splay); return false; } } if (debugTime) { afterRemoveTime = System.currentTimeMillis(); removeTime += afterRemoveTime-beforeRemoveTime; if (debug>0) System.out.println("Splay Tree remove time = "+removeTime/count+" ms"); } contains = splay.contains(INVALID); removed = splay.remove(INVALID); if (contains || removed) { System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); count++; if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); if (debugTime) beforeAddTime = System.currentTimeMillis(); for (int i=unsorted.length-1; i>=0; i--) { int item = unsorted[i]; splay.add(item); if (validateStructure && !(splay.size()==(unsorted.length-i))) { System.err.println("YIKES!! "+item+" caused a size mismatch."); handleError(splay); return false; } if (validateContents && !splay.contains(item)) { System.err.println("YIKES!! "+item+" doesn't exists."); handleError(splay); return false; } } if (debugTime) { afterAddTime = System.currentTimeMillis(); addTime += afterAddTime-beforeAddTime; if (debug>0) System.out.println("Splay Tree add time = "+addTime/count+" ms"); } if (debugMemory) { afterMemory = DataStructures.getMemoryUse(); memory += afterMemory-beforeMemory; if (debug>0) System.out.println("Splay Tree memory use = "+(memory/count)+" bytes"); } contains = splay.contains(INVALID); removed = splay.remove(INVALID); if (contains || removed) { System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); if (debug>1) System.out.println(splay.toString()); lookupTime = 0L; beforeLookupTime = 0L; afterLookupTime = 0L; if (debugTime) beforeLookupTime = System.currentTimeMillis(); for (int item : unsorted) { splay.contains(item); } if (debugTime) { afterLookupTime = System.currentTimeMillis(); lookupTime += afterLookupTime-beforeLookupTime; if (debug>0) System.out.println("Splay Tree lookup time = "+lookupTime/count+" ms"); } if (debugTime) beforeRemoveTime = System.currentTimeMillis(); for (int i=0; i<unsorted.length; i++) { int item = unsorted[i]; splay.remove(item); if (validateStructure && !(splay.size()==((unsorted.length-1)-i))) { System.err.println("YIKES!! "+item+" caused a size mismatch."); handleError(splay); return false; } if (validateContents && splay.contains(item)) { System.err.println("YIKES!! "+item+" still exists."); handleError(splay); return false; } } if (debugTime) { afterRemoveTime = System.currentTimeMillis(); removeTime += afterRemoveTime-beforeRemoveTime; if (debug>0) System.out.println("Splay Tree remove time = "+removeTime/count+" ms"); } contains = splay.contains(INVALID); removed = splay.remove(INVALID); if (contains || removed) { System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); //sorted long addSortedTime = 0L; long removeSortedTime = 0L; long beforeAddSortedTime = 0L; long afterAddSortedTime = 0L; long beforeRemoveSortedTime = 0L; long afterRemoveSortedTime = 0L; if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); if (debugTime) beforeAddSortedTime = System.currentTimeMillis(); for (int i=0; i<sorted.length; i++) { int item = sorted[i]; splay.add(item); if (validateStructure && !(splay.size()==(i+1))) { System.err.println("YIKES!! "+item+" caused a size mismatch."); handleError(splay); return false; } if (validateContents && !splay.contains(item)) { System.err.println("YIKES!! "+item+" doesn't exist."); handleError(splay); return false; } } if (debugTime) { afterAddSortedTime = System.currentTimeMillis(); addSortedTime += afterAddSortedTime-beforeAddSortedTime; if (debug>0) System.out.println("Splay Tree add time = "+addSortedTime+" ms"); } if (debugMemory) { afterMemory = DataStructures.getMemoryUse(); memory += afterMemory-beforeMemory; if (debug>0) System.out.println("Splay Tree memory use = "+(memory/(count+1))+" bytes"); } contains = splay.contains(INVALID); removed = splay.remove(INVALID); if (contains || removed) { System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); if (debug>1) System.out.println(splay.toString()); lookupTime = 0L; beforeLookupTime = 0L; afterLookupTime = 0L; if (debugTime) beforeLookupTime = System.currentTimeMillis(); for (int item : sorted) { splay.contains(item); } if (debugTime) { afterLookupTime = System.currentTimeMillis(); lookupTime += afterLookupTime-beforeLookupTime; if (debug>0) System.out.println("Splay Tree lookup time = "+lookupTime/(count+1)+" ms"); } if (debugTime) beforeRemoveSortedTime = System.currentTimeMillis(); for (int i=sorted.length-1; i>=0; i--) { int item = sorted[i]; splay.remove(item); if (validateStructure && !(splay.size()==i)) { System.err.println("YIKES!! "+item+" caused a size mismatch."); handleError(splay); return false; } if (validateContents && splay.contains(item)) { System.err.println("YIKES!! "+item+" still exists."); handleError(splay); return false; } } if (debugTime) { afterRemoveSortedTime = System.currentTimeMillis(); removeSortedTime += afterRemoveSortedTime-beforeRemoveSortedTime; if (debug>0) System.out.println("Splay Tree remove time = "+removeSortedTime+" ms"); } contains = splay.contains(INVALID); removed = splay.remove(INVALID); if (contains || removed) { System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); return false; } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed); if (testResults[testIndex]==null) testResults[testIndex] = new long[6]; testResults[testIndex][0]+=addTime/count; testResults[testIndex][1]+=removeTime/count; testResults[testIndex][2]+=addSortedTime; testResults[testIndex][3]+=removeSortedTime; testResults[testIndex][4]+=lookupTime/(count+1); testResults[testIndex++][5]+=memory/(count+1); if (debug>1) System.out.println(); } return true; }private static boolean testSuffixTree() { { //Suffix Tree if (debug>1) System.out.println("Suffix Tree."); String bookkeeper = "bookkeeper"; SuffixTree<String> tree = new SuffixTree<String>(bookkeeper); if (debug>1) System.out.println(tree.toString()); if (debug>1) System.out.println(tree.getSuffixes()); boolean exists = tree.doesSubStringExist(bookkeeper); if (!exists) { System.err.println("YIKES!! "+bookkeeper+" doesn't exists."); handleError(tree); return false; } String failed = "booker"; exists = tree.doesSubStringExist(failed); if (exists) { System.err.println("YIKES!! "+failed+" exists."); handleError(tree); return false; } String pass = "kkee"; exists = tree.doesSubStringExist(pass); if (!exists) { System.err.println("YIKES!! "+pass+" doesn't exists."); handleError(tree); return false; } if (debug>1) System.out.println(); } return true; }