区间树
可以统计某个区间对应的重复的区间
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;
}