`

java 关于区间树,KD树,线段树,伸展树,后缀树,红黑树的几段代码

 
阅读更多

区间树

可以统计某个区间对应的重复的区间

 

package com.jwetherell.algorithms.data_structures;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;


/**
 * An interval tree is an ordered tree data structure to hold intervals. Specifically, it 
 * allows one to efficiently find all intervals that overlap with any given interval or point.
 * 
 * http://en.wikipedia.org/wiki/Interval_tree
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */
public class IntervalTree<O extends Object> {

    private Interval<O> root = null;

    private static final Comparator<IntervalData<?>> startComparator = new Comparator<IntervalData<?>>(){
        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(IntervalData<?> arg0, IntervalData<?> arg1) {
            if (arg0.start<arg1.start) return -1;
            if (arg1.start<arg0.start) return 1;
            return 0;
        }
    };

    private static final Comparator<IntervalData<?>> endComparator = new Comparator<IntervalData<?>>(){
        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(IntervalData<?> arg0, IntervalData<?> arg1) {
            if (arg0.end<arg1.end) return -1;
            if (arg1.end<arg0.end) return 1;
            return 0;
        }
    };


    /**
     * Create interval tree from list of IntervalData objects;
     * 
     * @param intervals is a list of IntervalData objects
     */
    public IntervalTree(List<IntervalData<O>> intervals) {
        if (intervals.size()<=0) return;

        root = createFromList(intervals);
    }

    protected static final <O extends Object> Interval<O> createFromList(List<IntervalData<O>> intervals) {
        Interval<O> newInterval = new Interval<O>();
        int half = intervals.size()/2;
        IntervalData<O> middle = intervals.get(half);
        newInterval.center = ((middle.start+middle.end)/2);
        List<IntervalData<O>> leftIntervals = new ArrayList<IntervalData<O>>();
        List<IntervalData<O>> rightIntervals = new ArrayList<IntervalData<O>>();
        for (IntervalData<O> interval : intervals) {
            if (interval.end<newInterval.center) {
                leftIntervals.add(interval);
            } else if (interval.start>newInterval.center) {
                rightIntervals.add(interval);
            } else {
                newInterval.overlap.add(interval);
            }
        }
        if (leftIntervals.size()>0) newInterval.left = createFromList(leftIntervals);
        if (rightIntervals.size()>0) newInterval.right = createFromList(rightIntervals);
        return newInterval;
    }

    /**
     * Stabbing query
     * 
     * @param index to query for.
     * @return data at index.
     */
    public IntervalData<O> query(long index) {
        return root.query(index);
    }

    /**
     * Range query
     * 
     * @param start of range to query for.
     * @param end of range to query for.
     * @return data for range.
     */
    public IntervalData<O> query(long start, long end) {
        return root.query(start, end);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(IntervalTreePrinter.getString(this));
        return builder.toString();
    }


    protected static class IntervalTreePrinter {

        public static <O extends Object> String getString(IntervalTree<O> tree) {
            if (tree.root == null) return "Tree has no nodes.";
            return getString(tree.root, "", true);
        }

        private static <O extends Object> String getString(Interval<O> interval, String prefix, boolean isTail) {
            StringBuilder builder = new StringBuilder();
    
            builder.append( prefix + (isTail ? "└── " : "├── ") + interval.toString() + "\n" );
            List<Interval<O>> children = new ArrayList<Interval<O>>();
            if (interval.left!=null) children.add(interval.left);
            if (interval.right!=null) children.add(interval.right);
            if (children.size()>0) {
                for (int i = 0; i < children.size() - 1; i++) {
                    builder.append(getString(children.get(i), prefix + (isTail ? "    " : "│   "), false));
                }
                if (children.size() > 0) {
                    builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? "    " : "│   "), true));
                }
            }
    
            return builder.toString();
        }
    }

    public static final class Interval<O> {

        private long center = Long.MIN_VALUE;
        private Interval<O> left = null;
        private Interval<O> right = null;
        private Set<IntervalData<O>> overlap = new TreeSet<IntervalData<O>>(startComparator);


        /**
         * Stabbing query
         * 
         * @param index to query for.
         * @return data at index.
         */
        public IntervalData<O> query(long index) {
            IntervalData<O> results = null;
            if (index<center) {
                //overlap is sorted by start point
                for (IntervalData<O> data : overlap) {
                    if (data.start>index) break;

                    IntervalData<O> temp = data.query(index);
                    if (results==null && temp!=null) results = temp;
                    else if (temp!=null) results.combined(temp);
                }
            } else if (index>=center) {
                //overlapEnd is sorted by end point
                Set<IntervalData<O>> overlapEnd = new TreeSet<IntervalData<O>>(endComparator);
                overlapEnd.addAll(overlap);
                for (IntervalData<O> data : overlapEnd) {
                    if (data.end<index) break;

                    IntervalData<O> temp = data.query(index);
                    if (results==null && temp!=null) results = temp;
                    else if (temp!=null) results.combined(temp);
                }
            }
            if (index<center) {
                if (left!=null) {
                    IntervalData<O> temp = left.query(index);
                    if (results==null && temp!=null) results = temp;
                    else if (temp!=null) results.combined(temp);
                }
            } else if (index>=center) {
                if (right!=null) {
                    IntervalData<O> temp = right.query(index);
                    if (results==null && temp!=null) results = temp;
                    else if (temp!=null) results.combined(temp);
                }
            }
            return results;
        }

        /**
         * Range query
         * 
         * @param start of range to query for.
         * @param end of range to query for.
         * @return data for range.
         */
        public IntervalData<O> query(long start, long end) {
            IntervalData<O> results = null;
            for (IntervalData<O> data : overlap) {
                if (data.start > end) break; 
                IntervalData<O> temp = data.query(start,end);
                if (results==null && temp!=null) results = temp;
                else if (temp!=null) results.combined(temp);
            }
            if (left!=null && start<center) {
                IntervalData<O> temp = left.query(start,end);
                if (temp!=null && results==null) results = temp;
                else if (temp!=null) results.combined(temp);
            }
            if (right!=null && end>=center) {
                IntervalData<O> temp = right.query(start,end);
                if (temp!=null && results==null) results = temp;
                else if (temp!=null) results.combined(temp);
            }
            return results;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("Center=").append(center);
            builder.append(" Set=").append(overlap);
            return builder.toString();
        }
    }

    /**
     * Data structure representing an interval.
     */
    public static final class IntervalData<O> implements Comparable<IntervalData<O>>{

        private long start = Long.MIN_VALUE;
        private long end = Long.MAX_VALUE;
        private Set<O> set = new TreeSet<O>(); //Sorted


        /**
         * Interval data using O as it's unique identifier
         * @param object Object which defines the interval data
         */
        public IntervalData(long index, O object) {
            this.start = index;
            this.end = index;
            this.set.add(object);
        }

        /**
         * Interval data using O as it's unique identifier
         * @param object Object which defines the interval data
         */
        public IntervalData(long start, long end, O object) {
            this.start = start;
            this.end = end;
            this.set.add(object);
        }

        /**
         * Interval data list which should all be unique
         * @param list of interval data objects
         */
        public IntervalData(long start, long end, Set<O> set) {
            this.start = start;
            this.end = end;
            this.set = set;

            //Make sure they are unique
            Iterator<O> iter = set.iterator();
            while (iter.hasNext()) {
                O obj1 = iter.next();
                O obj2 = null;
                if (iter.hasNext()) obj2 = iter.next();
                if (obj1.equals(obj2)) throw new InvalidParameterException("Each interval data in the list must be unique.");
            }
        }

        /**
         * Clear the indices.
         */
        public void clear() {
            this.start = Long.MIN_VALUE;
            this.end = Long.MAX_VALUE;
            this.set.clear();
        }

        /**
         * Combined this IntervalData with data.
         * 
         * @param data to combined with.
         * @return Data which represents the combination.
         */
        public IntervalData<O> combined(IntervalData<O> data) {
            if (data.start<this.start) this.start = data.start;
            if (data.end>this.end) this.end = data.end;
            this.set.addAll(data.set);
            return this;
        }

        /**
         * Deep copy of data.
         * 
         * @return deep copy.
         */
        public IntervalData<O> copy() {
            Set<O> listCopy = new TreeSet<O>();
            listCopy.addAll(set);
            return new IntervalData<O>(start,end,listCopy);
        }

        /**
         * Query inside this data object.
         * 
         * @param start of range to query for.
         * @param end of range to query for.
         * @return Data queried for or NULL if it doesn't match the query.
         */
        public IntervalData<O> query(long index) {
            if (index<this.start || index>this.end) {
                //Ignore
            } else {
                return copy();
            }
            return null;
        }

        /**
         * Query inside this data object.
         * 
         * @param start of range to query for.
         * @param end of range to query for.
         * @return Data queried for or NULL if it doesn't match the query.
         */
        public IntervalData<O> query(long start, long end) {
            if (end<this.start || start>this.end) {
                //Ignore
            } else {
                return copy();
            }
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof IntervalData)) return false;
            @SuppressWarnings("unchecked")
            IntervalData<O> data = (IntervalData<O>) obj;
            if (this.start==data.start && this.end==data.end) {
                if (this.set.size()!=data.set.size()) return false;
                for (O o : set) {
                    if (!data.set.contains(o)) return false;
                }
                return true;
            }
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(IntervalData<O> d) {
            if (this.end < d.end) return -1;
            if (d.end < this.end) return 1;
            return 0;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append(start).append("->").append(end);
            builder.append(" set=").append(set);
            return builder.toString();
        }
    }
}

 

KD树,统计某个点最近的区间

package com.jwetherell.algorithms.data_structures;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;


/**
 * A k-d tree (short for k-dimensional tree) is a space-partitioning data structure for organizing 
 * points in a k-dimensional space. k-d trees are a useful data structure for several applications, 
 * such as searches involving a multidimensional search key (e.g. range searches and nearest neighbor 
 * searches). k-d trees are a special case of binary space partitioning trees.
 * 
 * http://en.wikipedia.org/wiki/K-d_tree
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */
public class KdTree<T extends KdTree.XYZPoint> {

    private int k = 3;
    private KdNode root = null;

    private static final Comparator<XYZPoint> X_COMPARATOR = new Comparator<XYZPoint>() {
        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(XYZPoint o1, XYZPoint o2) {
            if (o1.x<o2.x) return -1;
            if (o1.x>o2.x) return 1;
            return 0;
        }
    };

    private static final Comparator<XYZPoint> Y_COMPARATOR = new Comparator<XYZPoint>() {
        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(XYZPoint o1, XYZPoint o2) {
            if (o1.y<o2.y) return -1;
            if (o1.y>o2.y) return 1;
            return 0;
        }
    };

    private static final Comparator<XYZPoint> Z_COMPARATOR = new Comparator<XYZPoint>() {
        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(XYZPoint o1, XYZPoint o2) {
            if (o1.z<o2.z) return -1;
            if (o1.z>o2.z) return 1;
            return 0;
        }
    };

    protected static final int X_AXIS = 0;
    protected static final int Y_AXIS = 1;
    protected static final int Z_AXIS = 2;


    /**
     * Default constructor.
     */
    public KdTree() { }

    /**
     * More efficient constructor.
     * 
     * @param list of XYZPoints.
     */
    public KdTree(List<XYZPoint> list) { 
        root = createNode(list, k, 0);
    }

    /**
     * Create node from list of XYZPoints.
     * 
     * @param list of XYZPoints.
     * @param k of the tree.
     * @param depth depth of the node.
     * @return node created.
     */
    private static KdNode createNode(List<XYZPoint> list, int k, int depth) {
        if (list==null || list.size()==0) return null;

        int axis = depth % k;
        if (axis==X_AXIS) Collections.sort(list, X_COMPARATOR);
        else if (axis==Y_AXIS) Collections.sort(list, Y_COMPARATOR);
        else Collections.sort(list, Z_COMPARATOR);

        int mediaIndex = list.size()/2;
        KdNode node = new KdNode(k,depth,list.get(mediaIndex));
        if (list.size()>0) {
            if ((mediaIndex-1)>=0) {
                List<XYZPoint> less = list.subList(0, mediaIndex);
                if (less.size()>0) {
                    node.lesser = createNode(less,k,depth+1);
                    node.lesser.parent = node;
                }
            }
            if ((mediaIndex+1)<=(list.size()-1)) {
                List<XYZPoint> more = list.subList(mediaIndex+1, list.size());
                if (more.size()>0) {
                    node.greater = createNode(more,k,depth+1);
                    node.greater.parent = node;
                }
            }
        }

        return node;
    }

    /**
     * Add value to the tree. Tree can contain multiple equal values.
     * 
     * @param value T to add to the tree.
     * @return True if successfully added to tree.
     */
    public boolean add(T value) {
        if (value==null) return false;

        if (root==null) {
            root = new KdNode(value);
            return true;
        }

        KdNode node = root;
        while (true) {
            if (KdNode.compareTo(node.depth, node.k, node.id, value)<=0) {
                //Lesser
                if (node.lesser==null) {
                    KdNode newNode = new KdNode(k,node.depth+1,value);
                    newNode.parent = node;
                    node.lesser = newNode;
                    break;
                } else {
                    node = node.lesser;
                }
            } else {
                //Greater
                if (node.greater==null) {
                    KdNode newNode = new KdNode(k,node.depth+1,value);
                    newNode.parent = node;
                    node.greater = newNode;
                    break;
                } else {
                    node = node.greater;
                }
            }
        }

        return true;
    }

    /**
     * Does the tree contain the value.
     * 
     * @param value T to locate in the tree.
     * @return True if tree contains value.
     */
    public boolean contains(T value) {
        if (value==null) return false;

        KdNode node = getNode(this,value);
        return (node!=null);
    }

    /**
     * Locate T in the tree.
     * 
     * @param tree to search.
     * @param value to search for.
     * @return KdNode or NULL if not found
     */
    private static final <T extends KdTree.XYZPoint> KdNode getNode(KdTree<T> tree, T value) {
        if (tree==null || tree.root==null || value==null) return null;

        KdNode node = tree.root;
        while (true) {
            if (node.id.equals(value)) {
                return node;
            } else if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) {
                //Greater
                if (node.greater==null) {
                    return null;
                } else {
                    node = node.greater;
                }
            } else {
                //Lesser
                if (node.lesser==null) {
                    return null;
                } else {
                    node = node.lesser;
                }
            }
        }
    }

    /**
     * Remove first occurrence of value in the tree.
     * 
     * @param value T to remove from the tree.
     * @return True if value was removed from the tree.
     */
    public boolean remove(T value) {
        if (value==null) return false;

        KdNode node = getNode(this,value);
        if (node==null) return false;

        KdNode parent = node.parent;
        if (parent!=null) {
            if (parent.lesser!=null && node.equals(parent.lesser)) {
                List<XYZPoint> nodes = getTree(node);
                if (nodes.size()>0) {
                    parent.lesser = createNode(nodes,node.k,node.depth);
                    if (parent.lesser!=null) {
                        parent.lesser.parent = parent;
                    }
                } else {
                    parent.lesser = null;
                }
            } else {
                List<XYZPoint> nodes = getTree(node);
                if (nodes.size()>0) {
                    parent.greater = createNode(nodes,node.k,node.depth);
                    if (parent.greater!=null) {
                        parent.greater.parent = parent;
                    }
                } else {
                    parent.greater = null;
                }
            }
        } else {
            //root
            List<XYZPoint> nodes = getTree(node);
            if (nodes.size()>0) root = createNode(nodes,node.k,node.depth);
            else root = null;
        }

        return true;
    }

    /**
     * Get the (sub) tree rooted at root.
     * 
     * @param root of tree to get nodes for.
     * @return points in (sub) tree, not including root.
     */
    private static final List<XYZPoint> getTree(KdNode root) {
        List<XYZPoint> list = new ArrayList<XYZPoint>();
        if (root==null) return list;

        if (root.lesser!=null) {
            list.add(root.lesser.id);
            list.addAll(getTree(root.lesser));
        }
        if (root.greater!=null) {
            list.add(root.greater.id);
            list.addAll(getTree(root.greater));
        }

        return list;
    }

    /**
     * K Nearest Neighbor search
     * 
     * @param K Number of neighbors to retrieve. Can return more than K, if last nodes are equal distances.
     * @param value to find neighbors of.
     * @return collection of T neighbors.
     */
    @SuppressWarnings("unchecked")
    public Collection<T> nearestNeighbourSearch(int K, T value) {
        if (value==null) return null;

        //Map used for results
        TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value));

        //Find the closest leaf node
        KdNode prev = null;
        KdNode node = root;
        while (node!=null) {
            if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) {
                //Greater
                prev = node;
                node = node.greater;
            } else {
                //Lesser
                prev = node;
                node = node.lesser;
            }
        }
        KdNode leaf = prev;

        if (leaf!=null) {
            //Used to not re-examine nodes
            Set<KdNode> examined = new HashSet<KdNode>();

            //Go up the tree, looking for better solutions
            node = leaf;
            while (node!=null) {
                //Search node
                searchNode(value,node,K,results,examined);
                node = node.parent;
            }
        }

        //Load up the collection of the results
        Collection<T> collection = new ArrayList<T>(K);
        for (KdNode kdNode : results) {
            collection.add((T)kdNode.id);
        }
        return collection;
    }

    private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) {
        examined.add(node);

        //Search node
        KdNode lastNode = null;
        Double lastDistance = Double.MAX_VALUE;
        if (results.size()>0) {
            lastNode = results.last();
            lastDistance = lastNode.id.euclideanDistance(value);
        }
        Double nodeDistance = node.id.euclideanDistance(value);
        if (nodeDistance.compareTo(lastDistance)<0) {
            if (results.size()==K && lastNode!=null) results.remove(lastNode);
            results.add(node);
        } else if (nodeDistance.equals(lastDistance)) {
            results.add(node);
        } else if (results.size()<K) {
            results.add(node);
        }
        lastNode = results.last();
        lastDistance = lastNode.id.euclideanDistance(value);

        int axis = node.depth % node.k;
        KdNode lesser = node.lesser;
        KdNode greater = node.greater;

        //Search children branches, if axis aligned distance is less than current distance
        if (lesser!=null && !examined.contains(lesser)) {
            examined.add(lesser);

            double nodePoint = Double.MIN_VALUE;
            double valuePlusDistance = Double.MIN_VALUE;
            if (axis==X_AXIS) {
                nodePoint = node.id.x;
                valuePlusDistance = value.x-lastDistance;
            } else if (axis==Y_AXIS) {
                nodePoint = node.id.y;
                valuePlusDistance = value.y-lastDistance;
            } else {
                nodePoint = node.id.z;
                valuePlusDistance = value.z-lastDistance;
            }
            boolean lineIntersectsCube = ((valuePlusDistance<=nodePoint)?true:false);

            //Continue down lesser branch
            if (lineIntersectsCube) searchNode(value,lesser,K,results,examined);
        }
        if (greater!=null && !examined.contains(greater)) {
            examined.add(greater);

            double nodePoint = Double.MIN_VALUE;
            double valuePlusDistance = Double.MIN_VALUE;
            if (axis==X_AXIS) {
                nodePoint = node.id.x;
                valuePlusDistance = value.x+lastDistance;
            } else if (axis==Y_AXIS) {
                nodePoint = node.id.y;
                valuePlusDistance = value.y+lastDistance;
            } else {
                nodePoint = node.id.z;
                valuePlusDistance = value.z+lastDistance;
            }
            boolean lineIntersectsCube = ((valuePlusDistance>=nodePoint)?true:false);

            //Continue down greater branch
            if (lineIntersectsCube) searchNode(value,greater,K,results,examined);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return TreePrinter.getString(this);
    }


    protected static class EuclideanComparator implements Comparator<KdNode> {

        private XYZPoint point = null;


        public EuclideanComparator(XYZPoint point) {
            this.point = point;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(KdNode o1, KdNode o2) {
            Double d1 = point.euclideanDistance(o1.id);
            Double d2 = point.euclideanDistance(o2.id);
            if (d1.compareTo(d2)<0) return -1;
            else if (d2.compareTo(d1)<0) return 1;
            return o1.id.compareTo(o2.id);
        }
    };

    public static class KdNode implements Comparable<KdNode> {

        private int k = 3;
        private int depth = 0;
        private XYZPoint id = null;
        private KdNode parent = null;
        private KdNode lesser = null;
        private KdNode greater = null;


        public KdNode(XYZPoint id) {
            this.id = id;
        }

        public KdNode(int k, int depth, XYZPoint id) {
            this(id);
            this.k = k;
            this.depth = depth;
        }

        public static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2) {
            int axis = depth % k;
            if (axis==X_AXIS) return X_COMPARATOR.compare(o1, o2);
            return Y_COMPARATOR.compare(o1, o2);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(Object obj) {
            if (obj==null) return false;
            if (!(obj instanceof KdNode)) return false;
            
            KdNode kdNode = (KdNode) obj;
            if (this.compareTo(kdNode)==0) return true; 
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(KdNode o) {
            return compareTo(depth, k, this.id, o.id);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("k=").append(k);
            builder.append(" depth=").append(depth);
            builder.append(" id=").append(id.toString());
            return builder.toString();
        }
    }

    public static class XYZPoint implements Comparable<XYZPoint> {

        private double x = Double.NEGATIVE_INFINITY;
        private double y = Double.NEGATIVE_INFINITY;
        private double z = Double.NEGATIVE_INFINITY;


        public XYZPoint(double x, double y) {
            this.x = x;
            this.y = y;
            this.z = 0;
        }

        public XYZPoint(double x, int y, double z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /**
         * Computes the Euclidean distance from this point to the other.
         * 
         * @param o1 other point.
         * @return euclidean distance.
         */
        public double euclideanDistance(XYZPoint o1) {
            return euclideanDistance(o1,this);
        }

        /**
         * Computes the Euclidean distance from one point to the other.
         * 
         * @param o1 first point.
         * @param o2 second point.
         * @return euclidean distance.
         */
        private static final double euclideanDistance(XYZPoint o1, XYZPoint o2) {
            return Math.sqrt(Math.pow((o1.x-o2.x),2)+Math.pow((o1.y-o2.y),2)+Math.pow((o1.z-o2.z),2));
        };

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (!(obj instanceof XYZPoint)) return false;

            XYZPoint xyzPoint = (XYZPoint) obj;
            int xComp = X_COMPARATOR.compare(this, xyzPoint);
            if (xComp!=0) return false;
            int yComp = Y_COMPARATOR.compare(this, xyzPoint);
            return (yComp==0);
        }
        
        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(XYZPoint o) {
            int xComp = X_COMPARATOR.compare(this, o);
            if (xComp!=0) return xComp;
            int yComp = Y_COMPARATOR.compare(this, o);
            return yComp;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("(");
            builder.append(x).append(", ");
            builder.append(y).append(", ");
            builder.append(z);
            builder.append(")");
            return builder.toString();
        }
    }

    protected static class TreePrinter {

        public static <T extends XYZPoint> String getString(KdTree<T> tree) {
            if (tree.root == null) return "Tree has no nodes.";
            return getString(tree.root, "", true);
        }

        private static <T extends Comparable<T>> String getString(KdNode node, String prefix, boolean isTail) {
            StringBuilder builder = new StringBuilder();

            if (node.parent!=null) {
                String side = "left";
                if (node.parent.greater!=null && node.id.equals(node.parent.greater.id)) side = "right";
                builder.append(prefix + (isTail ? "└── " : "├── ") + "[" + side + "] " + "depth=" + node.depth + " id=" + node.id + "\n");
            } else {
                builder.append(prefix + (isTail ? "└── " : "├── ") + "depth=" + node.depth + " id=" + node.id + "\n");
            }
            List<KdNode> children = null;
            if (node.lesser != null || node.greater != null) {
                children = new ArrayList<KdNode>(2);
                if (node.lesser != null) children.add(node.lesser);
                if (node.greater != null) children.add(node.greater);
            }
            if (children != null) {
                for (int i = 0; i < children.size() - 1; i++) {
                    builder.append(getString(children.get(i), prefix + (isTail ? "    " : "│   "), false));
                }
                if (children.size() >= 1) {
                    builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? "    " : "│   "), true));
                }
            }

            return builder.toString();
        }
    }
}

 

红黑树

package com.jwetherell.algorithms.data_structures;

import java.util.ArrayList;
import java.util.List;


/**
 * A red–black tree is a type of self-balancing binary search tree, a data structure 
 * used in computer science, typically to implement associative arrays. A red–black tree 
 * is a binary search tree that inserts and deletes in such a way that the tree is always 
 * reasonably balanced. Red-black trees are often compared with AVL trees. AVL trees are 
 * more rigidly balanced, they are faster than red-black trees for lookup intensive 
 * applications. However, red-black trees are faster for insertion and removal.
 * 
 * http://en.wikipedia.org/wiki/Red%E2%80%93black_tree
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */
public class RedBlackTree<T extends Comparable<T>> extends BinarySearchTree<T> implements BinarySearchTree.INodeCreator<T> {

    protected static final boolean BLACK = false;
    protected static final boolean RED = true;


    /**
     * Default constructor.
     */
    public RedBlackTree() {
        this.creator = this;
    }

    /**
     * Constructor with external Node creator.
     */
    public RedBlackTree(INodeCreator<T> creator) {
        super(creator);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Node<T> addValue(T id) {
        RedBlackNode<T> nodeAdded = null;
        boolean added = false;
        if (root == null) {
            //Case 1 - The current node is at the root of the tree.
            if (this.creator==null) {
                root = new RedBlackNode<T>(null, id, BLACK);
                root.lesser = new RedBlackNode<T>(root,null,BLACK);
                root.greater = new RedBlackNode<T>(root,null,BLACK);
            } else {
                root = this.creator.createNewNode(null, id);
                ((RedBlackNode<T>)root).color = BLACK;
                root.lesser = this.creator.createNewNode(root,null);
                ((RedBlackNode<T>)root.lesser).color = BLACK;
                root.greater = this.creator.createNewNode(root,null);
                ((RedBlackNode<T>)root.greater).color = BLACK;
            }
            nodeAdded = (RedBlackNode<T>) root;
            added = true;
        } else {
            //Insert node like a BST would
            Node<T> node = root;
            while (node != null) {
                if (node.id==null) {
                    node.id = id;
                    ((RedBlackNode<T>)node).color = RED;
                    if (this.creator==null) {
                        node.lesser = new RedBlackNode<T>(node,null,BLACK);
                        node.greater = new RedBlackNode<T>(node,null,BLACK);
                    } else {
                        node.lesser = this.creator.createNewNode(node,null);
                        ((RedBlackNode<T>)node.lesser).color = BLACK;
                        node.greater = this.creator.createNewNode(node,null);
                        ((RedBlackNode<T>)node.greater).color = BLACK;
                    }
                    nodeAdded = (RedBlackNode<T>) node;
                    added = true;
                    break;
                } else if (id.compareTo(node.id) <= 0) {
                    node = node.lesser;
                } else {
                    node = node.greater;
                }
            }
        }

        if (added==true) {
            balanceAfterInsert(nodeAdded);
            size++;
        }
        
        return nodeAdded;
    }

    /**
     * Post insertion balancing algorithm.
     * 
     * @param node to begin balancing at.
     * @return True if balanced.
     */
    private void balanceAfterInsert(RedBlackNode<T> node) {
        RedBlackNode<T> parent = (RedBlackNode<T>) node.parent;

        if (parent == null) {
            //Case 1 - The current node is at the root of the tree.
            node.color = BLACK;
            return;
        }

        if (parent.color == BLACK) {
            //Case 2 - The current node's parent is black, so property 4 (both children of every red node are black) is not invalidated.
            return;
        }

        RedBlackNode<T> grandParent = node.getGrandParent();
        RedBlackNode<T> uncle = node.getUncle();
        if (parent.color==RED && uncle.color==RED) {
            //Case 3 - If both the parent and the uncle are red, then both of them can be repainted black and the grandparent becomes 
            // red (to maintain property 5 (all paths from any given node to its leaf nodes contain the same number of black nodes)).
            parent.color=BLACK;
            uncle.color=BLACK;
            if (grandParent!=null) {
                grandParent.color=RED;
                balanceAfterInsert(grandParent);
            }
        } else {
            if (parent.color==RED && uncle.color==BLACK) {
                //Case 4 - The parent is red but the uncle is black; also, the current node is the right child of parent, and parent in turn 
                // is the left child of its parent grandparent.
                if (node.equals(parent.greater) && parent.equals(grandParent.lesser)) {
                    //right-left
                    rotateLeft(parent);
                    node = (RedBlackNode<T>) node.lesser; 

                    grandParent = node.getGrandParent();
                    parent = (RedBlackNode<T>) node.parent;
                    uncle = node.getUncle();
                } else if (node.equals(parent.lesser) && parent.equals(grandParent.greater)) {
                    //left-right
                    rotateRight(parent);
                    node = (RedBlackNode<T>) node.greater;   

                    grandParent = node.getGrandParent();
                    parent = (RedBlackNode<T>) node.parent;
                    uncle = node.getUncle();
                }
            }

            if (parent.color==RED && uncle.color==BLACK) {
                //Case 5 - The parent is red but the uncle is black, the current node is the left child of parent, and parent is the left child of its parent G.
                parent.color = BLACK;
                grandParent.color = RED;
                if (node.equals(parent.lesser) && parent.equals(grandParent.lesser)) {
                    //left-left
                    rotateRight(grandParent);
                } else if (node.equals(parent.greater) && parent.equals(grandParent.greater)) {
                    //right-right
                    rotateLeft(grandParent);
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Node<T> removeValue(T value) {
        RedBlackNode<T> nodeRemoved = (RedBlackNode<T>) super.getNode(value);
        if (nodeRemoved==null) return null;

        if (nodeRemoved.isLeaf()) {
            //No children
            nodeRemoved.id = null;
            if (nodeRemoved.parent==null) {
                root = null;
            } else {
                nodeRemoved.id = null;
                nodeRemoved.color = BLACK;
                nodeRemoved.lesser = null;
                nodeRemoved.greater = null;
            }
        } else {
            //At least one child
            RedBlackNode<T> lesser = (RedBlackNode<T>) nodeRemoved.lesser;
            RedBlackNode<T> greater = (RedBlackNode<T>) nodeRemoved.greater;
            if (lesser.id!=null && greater.id!=null) {
                //Two children
                RedBlackNode<T> greatestInLesser = (RedBlackNode<T>) this.getGreatest(lesser);
                if (greatestInLesser==null || greatestInLesser.id==null) greatestInLesser = lesser;
                //Replace node with greatest in his lesser tree, which leaves us with only one child
                replaceValueOnly(nodeRemoved,greatestInLesser);
                nodeRemoved = greatestInLesser;
            }

            //Handle one child
            RedBlackNode<T> child = (RedBlackNode<T>)((nodeRemoved.lesser.id!=null)?nodeRemoved.lesser:nodeRemoved.greater);
            if (nodeRemoved.color==BLACK) {
                if (child.color==BLACK) {
                    nodeRemoved.color = RED;
                }
                boolean result = balanceAfterDelete(nodeRemoved);
                if (!result) return null;
            }
            replaceWithChild(nodeRemoved,child);
            if (root.equals(nodeRemoved) && nodeRemoved.isLeaf()) {
                //If we replaced the root with a leaf, just null out root
                root = null;
            }
        }

        size--;

        return nodeRemoved;
    }

    /**
     * Replace value of nodeToReplaceWith with nodeToReplace.
     * 
     * @param nodeToReplace will get value of nodeToReplaceWith.
     * @param nodeToReplaceWith will get value NULLed.
     */
    private void replaceValueOnly(RedBlackNode<T> nodeToReplace, RedBlackNode<T> nodeToReplaceWith) {
        nodeToReplace.id = nodeToReplaceWith.id;
        nodeToReplaceWith.id = null;
    }

    /**
     * Replace entire contents of nodeToReplace with nodeToReplaceWith.
     * 
     * @param nodeToReplace will get it's contents replace with nodeToReplaceWith contents.
     * @param nodeToReplaceWith will not be changed.
     */
    private void replaceWithChild(RedBlackNode<T> nodeToReplace, RedBlackNode<T> nodeToReplaceWith) {
        nodeToReplace.id = nodeToReplaceWith.id;
        nodeToReplace.color = nodeToReplaceWith.color;

        //root should always be black
        if (nodeToReplace.parent==null) nodeToReplace.color = BLACK; 

        nodeToReplace.lesser = nodeToReplaceWith.lesser;
        nodeToReplace.greater = nodeToReplaceWith.greater;
    }

    /**
     * Post delete balancing algorithm.
     * 
     * @param node to begin balancing at.
     * @return True if balanced or false if error.
     */
    private boolean balanceAfterDelete(RedBlackNode<T> node) {
        if (node.parent==null) {
            //Case 1 - node is the new root.
            return true;
        }

        RedBlackNode<T> parent = (RedBlackNode<T>) node.parent;
        RedBlackNode<T> sibling = node.getSibling();
        if (sibling.color==RED) {
            //Case 2 - sibling is red.
            parent.color = RED;
            sibling.color = BLACK;
            if (node.equals(parent.lesser)) {
                rotateLeft(parent);
                //Rotation, need to update parent/sibling
                parent = (RedBlackNode<T>) node.parent;
                sibling = node.getSibling();
            } else if (node.equals(parent.greater)) {
                rotateRight(parent);
                //Rotation, need to update parent/sibling
                parent = (RedBlackNode<T>) node.parent;
                sibling = node.getSibling();
            } else {
                System.err.println("Yikes! I'm not related to my parent.");
                return false;
            }
        }

        if (parent.color==BLACK && 
            sibling.color==BLACK && 
            ((RedBlackNode<T>)sibling.lesser).color==BLACK && 
            ((RedBlackNode<T>)sibling.greater).color==BLACK
        ) {
            //Case 3 - parent, sibling, and sibling's children are black.
            sibling.color = RED;
            boolean result = balanceAfterDelete(parent);
            if (!result) return false;
        } else if (parent.color==RED && 
                   sibling.color==BLACK && 
                   ((RedBlackNode<T>)sibling.lesser).color==BLACK && 
                   ((RedBlackNode<T>)sibling.greater).color==BLACK
        ) {
            //Case 4 - sibling and sibling's children are black, but parent is red.
            sibling.color = RED;
            parent.color = BLACK;
        } else {
            if (sibling.color==BLACK) {
                //Case 5 - sibling is black, sibling's left child is red, sibling's right child is black, and node is the left child of its parent. 
                if (node.equals(parent.lesser) && 
                    ((RedBlackNode<T>)sibling.lesser).color==RED && 
                    ((RedBlackNode<T>)sibling.greater).color==BLACK
                ) {
                    sibling.color = RED;
                    ((RedBlackNode<T>)sibling.lesser).color = RED;
                    rotateRight(sibling);
                    //Rotation, need to update parent/sibling
                    parent = (RedBlackNode<T>) node.parent;
                    sibling = node.getSibling();
                } else if (node.equals(parent.greater)  && 
                           ((RedBlackNode<T>)sibling.lesser).color==BLACK && 
                           ((RedBlackNode<T>)sibling.greater).color==RED
                ) {
                    sibling.color = RED;
                    ((RedBlackNode<T>)sibling.greater).color = RED;
                    rotateLeft(sibling);
                    //Rotation, need to update parent/sibling
                    parent = (RedBlackNode<T>) node.parent;
                    sibling = node.getSibling();
                }
            }

            //Case 6 - sibling is black, sibling's right child is red, and node is the left child of its parent.
            sibling.color = parent.color;
            parent.color = BLACK;
            if (node.equals(parent.lesser)) {
                ((RedBlackNode<T>)sibling.greater).color = BLACK;
                rotateLeft(node.parent);
            } else if (node.equals(parent.greater)) {
                ((RedBlackNode<T>)sibling.lesser).color = BLACK;
                rotateRight(node.parent);
            } else {
                System.err.println("Yikes! I'm not related to my parent. "+node.toString());
                return false;
            }
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean validate() {
        if (root==null) return true;

        if (((RedBlackNode<T>)root).color == RED) {
            //Root node should be black
            return false;
        }

        return this.validateNode(root);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean validateNode(Node<T> node) {
        RedBlackNode<T> rbNode = (RedBlackNode<T>) node;
        RedBlackNode<T> lesser = (RedBlackNode<T>) rbNode.lesser;
        RedBlackNode<T> greater = (RedBlackNode<T>) rbNode.greater;

        if (rbNode.isLeaf() && rbNode.color==RED) {
            //Leafs should not be red
            return false;
        }

        if (rbNode.color==RED) {
            //You should not have two red nodes in a row
            if (lesser.color==RED) return false;
            if (greater.color==RED) return false;
        }

        if (!lesser.isLeaf()) {
            //Check BST property
            boolean lesserCheck = lesser.id.compareTo(rbNode.id)<=0;
            if (!lesserCheck) return false;
            //Check red-black property
            lesserCheck = this.validateNode(lesser);
            if (!lesserCheck) return false;
        }

        if (!greater.isLeaf()) {
            //Check BST property
            boolean greaterCheck = greater.id.compareTo(rbNode.id)>0;
            if (!greaterCheck) return false;
            //Check red-black property
            greaterCheck = this.validateNode(greater);
            if (!greaterCheck) return false;
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return RedBlackTreePrinter.getString(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Node<T> createNewNode(Node<T> parent, T id) {
        return (new RedBlackNode<T>(parent, id, BLACK));
    }

    protected static class RedBlackNode<T extends Comparable<T>> extends Node<T> {

        protected boolean color = BLACK;


        protected RedBlackNode(Node<T> parent, T id, boolean color) {
            super(parent,id);
            this.color = color;
        }

        protected RedBlackNode<T> getGrandParent() {
            if (parent==null || parent.parent==null) return null; 
            return (RedBlackNode<T>) parent.parent;
        }

        protected RedBlackNode<T> getUncle() {
            RedBlackNode<T> grandParent = getGrandParent();
            if (grandParent == null) return null;
            if (grandParent.lesser!=null && grandParent.lesser.equals(parent)) {
                return (RedBlackNode<T>) grandParent.greater;
            } else if (grandParent.greater!=null && grandParent.greater.equals(parent)) {
                return (RedBlackNode<T>) grandParent.lesser;
            }
            return null;
        }

        protected RedBlackNode<T> getSibling() {
            if (parent==null) return null;
            if (parent.lesser.equals(this)) {
                return (RedBlackNode<T>) parent.greater;
            } else if (parent.greater.equals(this)) {
                return (RedBlackNode<T>) parent.lesser;
            } else {
                System.err.println("Yikes! I'm not my parents child.");
            }
            return null;
        }

        protected boolean isLeaf() {
            if (lesser!=null) return false;
            if (greater!=null) return false;
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return "value=" + id + 
                   " color=" + ((color==RED)?"RED":"BLACK") + 
                   " isLeaf=" + isLeaf() + 
                   " parent=" + ((parent != null) ? parent.id : "NULL") + 
                   " lesser=" + ((lesser != null) ? lesser.id : "NULL") + 
                   " greater=" + ((greater != null) ? greater.id : "NULL");
        }
    }

    protected static class RedBlackTreePrinter {

        public static <T extends Comparable<T>> String getString(RedBlackTree<T> tree) {
            if (tree.root == null) return "Tree has no nodes.";
            return getString((RedBlackNode<T>)tree.root, "", true);
        }

        public static <T extends Comparable<T>> String getString(RedBlackNode<T> node) {
            if (node == null) return "Sub-tree has no nodes.";
            return getString(node, "", true);
        }

        private static <T extends Comparable<T>> String getString(RedBlackNode<T> node, String prefix, boolean isTail) {
            StringBuilder builder = new StringBuilder();

            builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + ((node.color==RED)?"RED":"BLACK") + ") " + node.id + "\n");
            List<Node<T>> children = null;
            if (node.lesser != null || node.greater != null) {
                children = new ArrayList<Node<T>>(2);
                if (node.lesser != null) children.add(node.lesser);
                if (node.greater != null) children.add(node.greater);
            }
            if (children != null) {
                for (int i = 0; i < children.size() - 1; i++) {
                    builder.append(getString((RedBlackNode<T>)children.get(i), prefix + (isTail ? "    " : "│   "), false));
                }
                if (children.size() >= 1) {
                    builder.append(getString((RedBlackNode<T>)children.get(children.size() - 1), prefix + (isTail ? "    " : "│   "), true));
                }
            }

            return builder.toString();
        }
    }
}

 

线段树,可以统计线段的最小值,最大值,和等

package com.jwetherell.algorithms.data_structures;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;


/**
 * Segment tree using objects and pointers. A segment tree is a tree data
 * structure for storing intervals, or segments. It allows querying which of the
 * stored segments contain a given point. It is, in principle, a static
 * structure; that is, its content cannot be modified once the structure is
 * built.
 * 
 * http://en.wikipedia.org/wiki/Segment_tree
 * 
 * This class is meant to be somewhat generic, all you'd have to do is extend the 
 * Data abstract class to store your custom data. I've also included a range minimum, 
 * range maximum, range sum, and interval stabbing implementations.
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */
public abstract class SegmentTree<D extends SegmentTree.Data> {

    protected Segment<D> root = null;


    /**
     * Stabbing query
     * 
     * @param index to query for.
     * @return data at index.
     */
    public abstract D query(long index);

    /**
     * Range query
     * 
     * @param start of range to query for.
     * @param end of range to query for.
     * @return data for range.
     */
    public abstract D query(long start, long end);

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(SegmentTreePrinter.getString(this));
        return builder.toString();
    }


    public abstract static class Data implements Comparable<Data> {

        protected long start = Long.MIN_VALUE;
        protected long end = Long.MAX_VALUE;


        /**
         * Constructor for data at index.
         * 
         * @param index of data.
         */
        public Data(long index) {
            this.start = index;
            this.end = index;
        }

        /**
         * Constructor for data at range.
         * 
         * @param start of range for data.
         * @param end of range for data.
         */
        public Data(long start, long end) {
            this.start = start;
            this.end = end;
        }

        /**
         * Clear the indices.
         */
        public void clear() {
            start = Long.MIN_VALUE;
            end = Long.MAX_VALUE;
        }

        /**
         * Combined this data with data.
         * 
         * @param data to combined with.
         * @return Data which represents the combination.
         */
        public abstract Data combined(Data data);

        /**
         * Deep copy of data.
         * 
         * @return deep copy.
         */
        public abstract Data copy();

        /**
         * Query inside this data object.
         * 
         * @param start of range to query for.
         * @param end of range to query for.
         * @return Data queried for or NULL if it doesn't match the query.
         */
        public abstract Data query(long start, long end);

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append(start).append("->").append(end);
            return builder.toString();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(Data d) {
            if (this.end < d.end) return -1;
            if (d.end < this.end) return 1;
            return 0;
        }


        /**
         * Data structure representing points in the x,y space and their location
         * in the quadrants.
         */
        public static final class QuadrantData extends Data {

            public long quad1 = 0;
            public long quad2 = 0;
            public long quad3 = 0;
            public long quad4 = 0;


            public QuadrantData(long index) {
                super(index);
            }

            public QuadrantData(long start, long end) {
                super(start,end);
            }

            public QuadrantData(long index, long quad1, long quad2, long quad3, long quad4) {
                super(index);

                this.quad1 = quad1;
                this.quad2 = quad2;
                this.quad3 = quad3;
                this.quad4 = quad4;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void clear() {
                super.clear();

                quad1 = 0;
                quad2 = 0;
                quad3 = 0;
                quad4 = 0;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data combined(Data data) {
                QuadrantData q = null;
                if (data instanceof QuadrantData) {
                    q = (QuadrantData) data;
                    this.combined(q);
                }
                return this;
            }

            /**
             * Combined specific to quadrant data.
             * 
             * @param data to combined.
             */
            private void combined(QuadrantData data) {
                this.quad1 += data.quad1;
                this.quad2 += data.quad2;
                this.quad3 += data.quad3;
                this.quad4 += data.quad4;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public QuadrantData copy() {
                QuadrantData copy = new QuadrantData(start,end);
                copy.quad1 = this.quad1;
                copy.quad2 = this.quad2;
                copy.quad3 = this.quad3;
                copy.quad4 = this.quad4;
                return copy;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data query(long start, long end) {
                return copy();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append(quad1).append(",");
                builder.append(quad2).append(",");
                builder.append(quad3).append(",");
                builder.append(quad4);
                return builder.toString();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean equals(Object obj) {
                if (!(obj instanceof QuadrantData)) return false;
                QuadrantData data = (QuadrantData) obj;
                if (this.start==data.start && this.end==data.end && 
                    this.quad1==data.quad1 && this.quad2==data.quad2 && this.quad3==data.quad3 && this.quad4==data.quad4) 
                {
                    return true;
                }
                return false;
            }
        }

        /**
         * Data structure representing maximum in the range.
         */
        public static final class RangeMaximumData<N extends Number> extends Data {

            public N maximum = null;


            public RangeMaximumData(long index) {
                super(index);
            }

            public RangeMaximumData(long start, long end) {
                super(start,end);
            }

            public RangeMaximumData(long index, N number) {
                super(index);

                this.maximum = number;
            }

            public RangeMaximumData(long start, long end, N number) {
                super(start,end);

                this.maximum = number;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            @SuppressWarnings("unchecked")
            public Data combined(Data data) {
                RangeMaximumData<N> q = null;
                if (data instanceof RangeMaximumData) {
                    q = (RangeMaximumData<N>) data;
                    this.combined(q);
                }
                return this;
            }

            /**
             * Combined for range maximum specific data.
             * 
             * @param data resulted from the combination.
             */
            private void combined(RangeMaximumData<N> data) {
                if (this.maximum==null && data.maximum==null) return;
                else if (this.maximum!=null && data.maximum==null) return;
                else if (this.maximum==null && data.maximum!=null) this.maximum = data.maximum;
                else if (data.maximum.doubleValue() > this.maximum.doubleValue()) {
                    this.maximum = data.maximum;
                }
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data copy() {
                return new RangeMaximumData<N>(start,end,maximum);
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data query(long start, long end) {
                return copy();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append("maximum=").append(maximum);
                return builder.toString();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean equals(Object obj) {
                if (!(obj instanceof RangeMaximumData)) return false;
                @SuppressWarnings("unchecked")
                RangeMaximumData<N> data = (RangeMaximumData<N>) obj;
                if (this.start==data.start && this.end==data.end && this.maximum.equals(data.maximum)) return true;
                return false;
            }
        }

        /**
         * Data structure representing minimum in the range.
         */
        public static final class RangeMinimumData<N extends Number> extends Data {

            public N minimum = null;


            public RangeMinimumData(long index) {
                super(index);
            }

            public RangeMinimumData(long start, long end) {
                super(start,end);
            }

            public RangeMinimumData(long index, N number) {
                super(index);

                this.minimum = number;
            }

            public RangeMinimumData(long start, long end, N number) {
                super(start,end);

                this.minimum = number;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void clear() {
                super.clear();

                minimum = null;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            @SuppressWarnings("unchecked")
            public Data combined(Data data) {
                RangeMinimumData<N> q = null;
                if (data instanceof RangeMinimumData) {
                    q = (RangeMinimumData<N>) data;
                    this.combined(q);
                }
                return this;
            }

            /**
             * Combined specific to range minimum specific data.
             * 
             * @param data resulted from combination.
             */
            private void combined(RangeMinimumData<N> data) {
                if (this.minimum==null && data.minimum==null) return;
                else if (this.minimum!=null && data.minimum==null) return;
                else if (this.minimum==null && data.minimum!=null) this.minimum = data.minimum;
                else if (data.minimum.doubleValue() < this.minimum.doubleValue()) {
                    this.minimum = data.minimum;
                }
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data copy() {
                return new RangeMinimumData<N>(start,end,minimum);
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data query(long start, long end) {
                return copy();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append("minimum=").append(minimum);
                return builder.toString();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean equals(Object obj) {
                if (!(obj instanceof RangeMinimumData)) return false;
                @SuppressWarnings("unchecked")
                RangeMinimumData<N> data = (RangeMinimumData<N>) obj;
                if (this.start==data.start && this.end==data.end && this.minimum.equals(data.minimum)) return true;
                return false;
            }
        }

        /**
         * Data structure representing sum of the range.
         */
        public static final class RangeSumData<N extends Number> extends Data {

            public N sum = null;


            public RangeSumData(long index) {
                super(index);
            }

            public RangeSumData(long start, long end) {
                super(start,end);
            }

            public RangeSumData(long index, N number) {
                super(index);

                this.sum = number;
            }

            public RangeSumData(long start, long end, N number) {
                super(start,end);

                this.sum = number;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void clear() {
                super.clear();

                sum = null;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            @SuppressWarnings("unchecked")
            public Data combined(Data data) {
                RangeSumData<N> q = null;
                if (data instanceof RangeSumData) {
                    q = (RangeSumData<N>) data;
                    this.combined(q);
                }
                return this;
            }

            /**
             * Combined specific to range sum specific data.
             * 
             * @param data resulted from combination.
             */
            @SuppressWarnings("unchecked")
            private void combined(RangeSumData<N> data) {
                if (this.sum==null && data.sum==null) return;
                else if (this.sum!=null && data.sum==null) return;
                else if (this.sum==null && data.sum!=null) this.sum = data.sum;
                else {
                    Double d1 = this.sum.doubleValue();
                    Double d2 = data.sum.doubleValue();
                    Double r = d1+d2;
                    this.sum = (N)r;
                }
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data copy() {
                return new RangeSumData<N>(start,end,sum);
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data query(long start, long end) {
                return copy();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append("sum=").append(sum);
                return builder.toString();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean equals(Object obj) {
                if (!(obj instanceof RangeSumData)) return false;
                @SuppressWarnings("unchecked")
                RangeSumData<N> data = (RangeSumData<N>) obj;
                if (this.start==data.start && this.end==data.end && this.sum.equals(data.sum)) return true;
                return false;
            }
        }

        /**
         * Data structure representing an interval.
         */
        public static final class IntervalData<O extends Object> extends Data {

            private Set<O> set = new TreeSet<O>(); //Sorted


            /**
             * Interval data using O as it's unique identifier
             * @param object Object which defines the interval data
             */
            public IntervalData(long index, O object) {
                super(index);

                this.set.add(object);
            }

            /**
             * Interval data using O as it's unique identifier
             * @param object Object which defines the interval data
             */
            public IntervalData(long start, long end, O object) {
                super(start,end);

                this.set.add(object);
            }

            /**
             * Interval data list which should all be unique
             * @param list of interval data objects
             */
            public IntervalData(long start, long end, Set<O> set) {
                super(start,end);

                this.set = set;

                //Make sure they are unique
                Iterator<O> iter = set.iterator();
                while (iter.hasNext()) {
                    O obj1 = iter.next();
                    O obj2 = null;
                    if (iter.hasNext()) obj2 = iter.next();
                    if (obj1.equals(obj2)) throw new InvalidParameterException("Each interval data in the list must be unique.");
                }
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public void clear() {
                super.clear();

                this.set.clear();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            @SuppressWarnings("unchecked")
            public Data combined(Data data) {
                IntervalData<O> q = null;
                if (data instanceof IntervalData) {
                    q = (IntervalData<O>) data;
                    this.combined(q);
                }
                return this;
            }

            /**
             * Combined for interval specific data.
             * 
             * @param data resulted from combination.
             */
            private void combined(IntervalData<O> data) {
                if (data.start<this.start) this.start = data.start;
                if (data.end>this.end) this.end = data.end;
                this.set.addAll(data.set);
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data copy() {
                Set<O> listCopy = new TreeSet<O>();
                listCopy.addAll(set);
                return new IntervalData<O>(start,end,listCopy);
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Data query(long start, long end) {
                if (end<this.start || start>this.end) {
                    //Ignore
                } else {
                    return copy();
                }
                return null;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append("set=").append(set);
                return builder.toString();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean equals(Object obj) {
                if (!(obj instanceof IntervalData)) return false;
                @SuppressWarnings("unchecked")
                IntervalData<O> data = (IntervalData<O>) obj;
                if (this.start==data.start && this.end==data.end) {
                    if (this.set.size()!=data.set.size()) return false;
                    for (O o : set) {
                        if (!data.set.contains(o)) return false;
                    }
                    return true;
                }
                return false;
            }
        }
    }

    /**
     * Data structure representing a segment.
     */
    protected abstract static class Segment<D extends Data> implements Comparable<Segment<D>> {

        protected Segment<D>[] segments = null;
        protected int length = 0;
        protected int half = 0;
        protected long start = 0;
        protected long end = 0;
        protected D data = null;
        protected int minLength = 0;


        public Segment(int minLength) {
            this.minLength = minLength;
        }

        /**
         * Query for data in range.
         * 
         * @param start of the range to query for.
         * @param end of range to query for.
         * @return Data in the range.
         */
        public abstract D query(long start, long end);

        protected boolean hasChildren() {
            return (segments!=null);
        }
        protected Segment<D> getLeftChild() {
            return segments[0];
        }
        protected Segment<D> getRightChild() {
            return segments[1];
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append(start).append("->");
            builder.append(end).append(" ");
            builder.append("Length=").append(length).append(" ");
            builder.append("Data={").append(data).append("}");
            return builder.toString();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(Segment<D> p) {
            if (this.end < p.end) return -1;
            if (p.end < this.end) return 1;
            return 0;
        }
    }

    protected static class SegmentTreePrinter {

        public static <D extends SegmentTree.Data> String getString(SegmentTree<D> tree) {
            if (tree.root == null) return "Tree has no nodes.";
            return getString(tree.root, "", true);
        }

        private static <D extends SegmentTree.Data> String getString(Segment<D> segment, String prefix, boolean isTail) {
            StringBuilder builder = new StringBuilder();

            builder.append( prefix + (isTail ? "└── " : "├── ") + segment.toString() + "\n" );
            List<Segment<D>> children = new ArrayList<Segment<D>>();
            if (segment.segments!=null) {
                for (Segment<D> c : segment.segments) children.add(c);
            }
            if (children != null) {
                for (int i = 0; i < children.size() - 1; i++) {
                    builder.append(getString(children.get(i), prefix + (isTail ? "    " : "│   "), false));
                }
                if (children.size() > 1) {
                    builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? "    " : "│   "), true));
                }
            }

            return builder.toString();
        }
    }

    /**
     * Flat segment tree is a variant of segment tree that is designed to store a collection of non-overlapping 
     * segments. This structure is efficient when you need to store values associated with 1 dimensional segments 
     * that never overlap with each other. The end points of stored segments are inclusive, that is, when a 
     * segment spans from 2 to 6, an arbitrary point x within that segment can take a value of 2 <= x <= 6.
     */
    public static final class FlatSegmentTree<D extends Data> extends SegmentTree<D> {

        public FlatSegmentTree(List<D> data) {
            this(data,1);
        }

        @SuppressWarnings("unchecked")
        public FlatSegmentTree(List<D> data, int minLength) {

            if (data.size()<=0) 
            	throw new InvalidParameterException("Segments list is empty.");

            Collections.sort(data); //Make sure they are sorted

            //Make sure they don't overlap
            if (data.size()>=2) {
            	for (int i=0; i<(data.size()-2); i++) {
            		Data s1 = data.get(i); 
            		Data s2 = data.get(i+1);
            		if (s1.end>s2.start) 
            			throw new InvalidParameterException("Segments are overlapping.");
            	}
            }

            //Check for gaps
            List<NonOverlappingSegment<D>> segments = new ArrayList<NonOverlappingSegment<D>>();
            for (int i=0; i<data.size(); i++) {
                if (i<data.size()-1) {
                    Data d1 = data.get(i); 
                    NonOverlappingSegment<D> s1 = new NonOverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1);
                    segments.add(s1);
                    Data d2 = data.get(i+1);
                    if (d2.start-d1.end>1) {
                        Data d3 = d1.copy();
                        d3.clear();
                        d3.start = d1.end+1;
                        d3.end = d2.start-1;
                        NonOverlappingSegment<D> s3 = new NonOverlappingSegment<D>(minLength,d3.start,d3.end,(D)d3); 
                        segments.add(s3);
                    }
                } else {
                    Data d1 = data.get(i); 
                    NonOverlappingSegment<D> s1 = new NonOverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1);
                    segments.add(s1);
                }
            }

            long start = segments.get(0).start;
            long end = segments.get(segments.size()-1).end;
            int length = (int)(end-start)+1;
            root = NonOverlappingSegment.createFromList(minLength,segments,start,length);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public D query(long index) {
            return this.query(index, index);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public D query(long start, long end) {
            if (root==null) return null;

            if (start<root.start) start = root.start;
            if (end>root.end) end = root.end;

            return (D)root.query(start, end);
        }


        /**
         * Data structure representing a non-overlapping segment.
         */
        protected static final class NonOverlappingSegment<D extends Data> extends Segment<D> {

            private Set<Segment<D>> set = new TreeSet<Segment<D>>();


            public NonOverlappingSegment(int minLength) {
                super(minLength);
            }

            public NonOverlappingSegment(int minLength, D data) {
                this(minLength,data.start,data.end,data);
            }

            @SuppressWarnings("unchecked")
            public NonOverlappingSegment(int minLength,long start, long end, D data) {
                super(minLength);
                this.start = start;
                this.end = end;
                this.length = ((int)(end-start))+1;
                if (data==null) return;
                this.data = ((D)data.copy());
            }

            @SuppressWarnings("unchecked")
            protected static <D extends Data> Segment<D> createFromList(int minLength, List<NonOverlappingSegment<D>> segments, long start, int length) {
                NonOverlappingSegment<D> segment = new NonOverlappingSegment<D>(minLength);
                segment.start = start;
                segment.end = start+(length-1);
                segment.length = length;

                for (Segment<D> s : segments) {
                    if (segment.data==null) segment.data = ((D)s.data.copy());
                    else segment.data.combined(s.data); //Update our data to reflect all children's data
                }

                //If segment is greater or equal to two, split data into children
                if (segment.length >= 2 && segment.length>=minLength) {
                    segment.half = segment.length / 2;
                    List<NonOverlappingSegment<D>> s1 = new ArrayList<NonOverlappingSegment<D>>();
                    List<NonOverlappingSegment<D>> s2 = new ArrayList<NonOverlappingSegment<D>>();
                    for (int i = 0; i < segments.size(); i++) {
                        NonOverlappingSegment<D> s = segments.get(i);
                        long middle = segment.start+segment.half;
                        if (s.end<middle) {
                            s1.add(s);
                        } else if (s.start>=middle) {
                            s2.add(s);
                        } else {
                            //Need to split across middle
                            NonOverlappingSegment<D> ss1 = new NonOverlappingSegment<D>(minLength,s.start,middle-1,s.data);
                            s1.add(ss1);
                            NonOverlappingSegment<D> ss2 = new NonOverlappingSegment<D>(minLength,middle,s.end,s.data);
                            s2.add(ss2);
                        }
                    }
                    Segment<D> sub1 = createFromList(minLength,s1,segment.start,segment.half);
                    Segment<D> sub2 = createFromList(minLength,s2,segment.start+segment.half,segment.length-segment.half);
                    segment.segments = new Segment[] { sub1, sub2 };
                } else if (segment.length<=minLength) {
                    for (Segment<D> s : segments) {
                        segment.set.add(s);
                    }
                }

                return segment;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            @SuppressWarnings("unchecked")
            public D query(long start, long end) {
                if (start == this.start && end == this.end) {
                    if (this.data==null) return null;
                    D dataToReturn = ((D)this.data.query(start,end));
                    return dataToReturn;
                } else if (!this.hasChildren()) {
                    if (end<this.start || start>this.end) {
                        //Ignore
                    } else {
                        D dataToReturn = null;
                        if (this.set.size()==0) return dataToReturn;
                        for (Segment<D> s : this.set) {
                            if (s.start >= start && s.end <= end) {
                                if (dataToReturn==null) dataToReturn = (D)s.data.query(start,end);
                                else dataToReturn.combined(s.data);
                            } else if (s.start <= start && s.end >= end) {
                                if (dataToReturn==null) dataToReturn = (D)s.data.query(start,end);
                                else dataToReturn.combined(s.data);
                            }
                        }
                        return dataToReturn;
                    }
                } else if (this.hasChildren()) {
                    if (start <= this.getLeftChild().end && end > this.getLeftChild().end) {
                        Data q1 = this.getLeftChild().query(start, getLeftChild().end);
                        Data q2 = this.getRightChild().query(getRightChild().start, end);
                        if (q1==null && q2==null) return null;
                        if (q1!=null && q2==null) return (D)q1;
                        if (q1==null && q2!=null) return (D)q2;
                        return ((D)q1.combined(q2));
                    } else if (start <= this.getLeftChild().end && end <= this.getLeftChild().end) {
                        return this.getLeftChild().query(start, end);
                    }
                    return this.getRightChild().query(start, end);
                }
                return null;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append("Set=").append(set);
                return builder.toString();
            }
        }
    }

    /**
     * Segment tree is a balanced-binary-tree based data structure efficient for detecting all intervals (or segments) 
     * that contain a given point. The segments may overlap with each other. The end points of stored segments are
     * inclusive, that is, when an interval spans from 2 to 6, an arbitrary point x within that interval can take a 
     * value of 2 <= x <=6.
     */
    public static final class DynamicSegmentTree<D extends Data> extends SegmentTree<D> {

        public DynamicSegmentTree(List<D> data) {
            this(data,1);
        }

        @SuppressWarnings("unchecked")
        public DynamicSegmentTree(List<D> data, int minLength) {
            if (data.size()<=0) return;

            //Check for gaps
            List<OverlappingSegment<D>> segments = new ArrayList<OverlappingSegment<D>>();
            for (int i=0; i<data.size(); i++) {
                if (i<data.size()-1) {
                    Data d1 = data.get(i); 
                    OverlappingSegment<D> s1 = new OverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1);
                    segments.add(s1);
                    Data d2 = data.get(i+1);
                    if (d2.start-d1.end>1) {
                        Data d3 = d1.copy();
                        d3.clear();
                        d3.start = d1.end+1;
                        d3.end = d2.start-1;
                        OverlappingSegment<D> s3 = new OverlappingSegment<D>(minLength,d3.start,d3.end,(D)d3); 
                        segments.add(s3);
                    }
                } else {
                    Data d1 = data.get(i); 
                    OverlappingSegment<D> s1 = new OverlappingSegment<D>(minLength,d1.start,d1.end,(D)d1);
                    segments.add(s1);
                }
            }

            //First start first
            Collections.sort(segments, 
                new Comparator<OverlappingSegment<D>>(){
                    @Override
                    public int compare(OverlappingSegment<D> arg0, OverlappingSegment<D> arg1) {
                        if (arg0.start<arg1.start) return -1;
                        if (arg1.start<arg0.start) return 1;
                        return 0;
                    }
                }
            );
            OverlappingSegment<D> startNode = segments.get(0);
            long start = startNode.start-1;
            OverlappingSegment<D> s1 = new OverlappingSegment<D>(minLength,start,startNode.start,null);
            segments.add(0,s1);

            //Last end last
            Collections.sort(segments, 
                new Comparator<OverlappingSegment<D>>(){
                    @Override
                    public int compare(OverlappingSegment<D> arg0, OverlappingSegment<D> arg1) {
                        if (arg0.end<arg1.end) return -1;
                        if (arg1.end<arg0.end) return 1;
                        return 0;
                    }
                }
            );
            OverlappingSegment<D> endNode = segments.get(segments.size()-1);
            long end = endNode.end+1;
            OverlappingSegment<D> s2 = new OverlappingSegment<D>(minLength,endNode.end,end,null);
            segments.add(s2);

            int length = (int)(end-start)+1;
            root = OverlappingSegment.createFromList(minLength,segments,start,length);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public D query(long index) {
            return this.query(index, index);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public D query(long start, long end) {
            if (root==null) return null;

            if (start<root.start) start = root.start;
            if (end>root.end) end = root.end;

            D result = root.query(start, end);
            return result;
        }


        /**
         * Data structure representing a possibly overlapping segment.
         */
        protected static final class OverlappingSegment<D extends Data> extends Segment<D> {

            //Separate range set for fast range queries
            protected Set<Segment<D>> range = new HashSet<Segment<D>>();


            public OverlappingSegment(int minLength) {
                super(minLength);
            }

            @SuppressWarnings("unchecked")
            public OverlappingSegment(int minLength, long start, long end, D data) {
                super(minLength);
                this.start = start;
                this.end = end;
                this.length = ((int)(end-start))+1;
                if (data==null) return;
                this.data = ((D)data.copy());
            }

            @SuppressWarnings("unchecked")
            protected static <D extends Data> Segment<D> createFromList(int minLength, List<OverlappingSegment<D>> segments, long start, int length) {
                OverlappingSegment<D> segment = new OverlappingSegment<D>(minLength);
                segment.start = start;
                segment.end = start+(length-1);
                segment.length = length;

                for (Segment<D> s : segments) {
                    if (s.data==null) continue;
                    if (s.end<segment.start || s.start>segment.end) {
                        //Ignore
                    } else {
                        segment.range.add(s);
                    }
                    if (s.start==segment.start && s.end==segment.end) {
                        if (segment.data==null) segment.data = ((D)s.data.copy());
                        else segment.data.combined(s.data); //Update our data to reflect all children's data
                    } else if (!segment.hasChildren() && s.start>=segment.start && s.end<=segment.end) {
                        if (segment.data==null) segment.data = ((D)s.data.copy());
                        else segment.data.combined(s.data); //Update our data to reflect all children's data
                    }
                }

                //If segment is greater or equal to two, split data into children
                if (segment.length >= 2 && segment.length>=minLength) {
                    segment.half = segment.length / 2;
                    List<OverlappingSegment<D>> s1 = new ArrayList<OverlappingSegment<D>>();
                    List<OverlappingSegment<D>> s2 = new ArrayList<OverlappingSegment<D>>();
                    for (int i = 0; i < segments.size(); i++) {
                        OverlappingSegment<D> s = segments.get(i);
                        long middle = segment.start+segment.half;
                        if (s.end<middle) {
                            s1.add(s);
                        } else if (s.start>=middle) {
                            s2.add(s);
                        } else {
                            //Need to split across middle
                            OverlappingSegment<D> ss1 = new OverlappingSegment<D>(minLength,s.start,middle-1,s.data);
                            s1.add(ss1);
                            OverlappingSegment<D> ss2 = new OverlappingSegment<D>(minLength,middle,s.end,s.data);
                            s2.add(ss2);
                        }
                    }
                    Segment<D> sub1 = createFromList(minLength,s1,segment.start,segment.half);
                    Segment<D> sub2 = createFromList(minLength,s2,segment.start+segment.half,segment.length-segment.half);
                    segment.segments = new Segment[] { sub1, sub2 };
                }

                return segment;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            @SuppressWarnings("unchecked")
            public D query(long start, long end) {
                D result = null;

                //Use the range data to make range queries faster
                if (start==this.start && end==this.end) {
                    for (Segment<D> s : this.range) {
                        D temp = (D)s.data.query(start, end);
                        if (temp!=null) {
                            if (result==null) result = (D)temp.copy();
                            else result.combined(temp);
                        }
                    }
                } else if (!this.hasChildren()) {
                    if (end<this.start || start>this.end) {
                        //Ignore
                    } else {
                        for (Segment<D> s : this.range) {
                            if (end<s.start || start>s.end) {
                                //Ignore
                            } else {
                                D temp = (D)s.data.query(start, end);
                                if (temp!=null) {
                                    if (result==null) result = (D)temp.copy();
                                    else result.combined(temp);
                                }
                            }
                        }
                    }
                } else {
                    long middle = this.start+this.half;
                    D temp = null;
                    if (start<middle && end>=middle) {
                        temp = this.getLeftChild().query(start, middle-1);
                        D temp2 = this.getRightChild().query(middle, end);
                        if (temp2!=null) {
                            if (temp==null) temp = (D)temp2.copy();
                            else temp.combined(temp2);
                        }
                    } else if (end<middle) {
                        temp = this.getLeftChild().query(start, end);
                    } else if (start>=middle) {
                        temp = this.getRightChild().query(start, end);
                    }
                    if (temp!=null) result = (D)temp.copy();
                }

                return result;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String toString() {
                StringBuilder builder = new StringBuilder();
                builder.append(super.toString()).append(" ");
                builder.append("Range=").append(range);
                return builder.toString();
            }
        }
    }
}
 

伸展树 把最近访问的结点统计旋转变为根结点

package com.jwetherell.algorithms.data_structures;


/**
 * A splay tree is a self-adjusting binary search tree (BST) with the additional
 * property that recently accessed elements are quick to access again.
 * 
 * http://en.wikipedia.org/wiki/Splay_tree
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */
public class SplayTree<T extends Comparable<T>> extends BinarySearchTree<T> {

    /**
     * {@inheritDoc}
     */
    @Override
    protected Node<T> addValue(T id) {
        Node<T> nodeToReturn = super.addValue(id);
        Node<T> nodeAdded = nodeToReturn;
        if (nodeAdded!=null) {
            //Splay the new node to the root position
            while (nodeAdded.parent!=null) {
                this.splay(nodeAdded);   
            }
        }
        return nodeToReturn;
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    protected Node<T> removeValue(T value) {
        Node<T> nodeToRemove = super.removeValue(value);
        if (nodeToRemove!=null) {
            if (nodeToRemove.parent!=null) {
                Node<T> nodeParent = nodeToRemove.parent;
                //Splay the parent node to the root position
                while (nodeParent.parent!=null) {
                    this.splay(nodeParent);   
                }
            }
        }
        return nodeToRemove;
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean contains(T value) {
        Node<T> node = getNode(value);
        if (node!=null) {
            //Splay the new node to the root position
            while (node.parent!=null) {
                this.splay(node);   
            }
            return true;
        }
        return false;
    }

    /**
     * Splay the tree at the node.
     * @param node to splay at.
     */
    private void splay(Node<T> node) {
        Node<T> parent = node.parent;
        Node<T> grandParent = (parent != null) ? parent.parent : null;
        if (parent == root) {
            // Zig step
            root = node;
            node.parent = null;

            if (node == parent.lesser) {
                parent.lesser = node.greater;
                if (node.greater != null) node.greater.parent = parent;

                node.greater = parent;
                parent.parent = node;
            } else {
                parent.greater = node.lesser;
                if (node.lesser != null) node.lesser.parent = parent;

                node.lesser = parent;
                parent.parent = node;
            }
        } else if (parent != null && grandParent != null) {
            Node<T> greatGrandParent = grandParent.parent;
            if (greatGrandParent != null && greatGrandParent.lesser == grandParent) {
                greatGrandParent.lesser = node;
                node.parent = greatGrandParent;
            } else if (greatGrandParent != null && greatGrandParent.greater == grandParent) {
                greatGrandParent.greater = node;
                node.parent = greatGrandParent;
            } else {
                // I am now root!
                root = node;
                node.parent = null;
            }

            if ((node == parent.lesser && parent == grandParent.lesser) || (node == parent.greater && parent == grandParent.greater)) {
                // Zig-zig step
                if (node == parent.lesser) {
                    Node<T> nodeGreater = node.greater;
                    node.greater = parent;
                    parent.parent = node;

                    parent.lesser = nodeGreater;
                    if (nodeGreater != null) nodeGreater.parent = parent;

                    Node<T> parentGreater = parent.greater;
                    parent.greater = grandParent;
                    grandParent.parent = parent;

                    grandParent.lesser = parentGreater;
                    if (parentGreater != null) parentGreater.parent = grandParent;
                } else {
                    Node<T> nodeLesser = node.lesser;
                    node.lesser = parent;
                    parent.parent = node;

                    parent.greater = nodeLesser;
                    if (nodeLesser != null) nodeLesser.parent = parent;

                    Node<T> parentLesser = parent.lesser;
                    parent.lesser = grandParent;
                    grandParent.parent = parent;

                    grandParent.greater = parentLesser;
                    if (parentLesser != null) parentLesser.parent = grandParent;
                }
            } else {
                // Zig-zag step
                if (node == parent.lesser) {
                    Node<T> nodeLesser = node.greater;
                    Node<T> nodeGreater = node.lesser;

                    node.greater = parent;
                    parent.parent = node;

                    node.lesser = grandParent;
                    grandParent.parent = node;

                    parent.lesser = nodeLesser;
                    if (nodeLesser != null) nodeLesser.parent = parent;

                    grandParent.greater = nodeGreater;
                    if (nodeGreater != null) nodeGreater.parent = grandParent;
                } else {
                    Node<T> nodeLesser = node.lesser;
                    Node<T> nodeGreater = node.greater;

                    node.lesser = parent;
                    parent.parent = node;

                    node.greater = grandParent;
                    grandParent.parent = node;

                    parent.greater = nodeLesser;
                    if (nodeLesser != null) nodeLesser.parent = parent;

                    grandParent.lesser = nodeGreater;
                    if (nodeGreater != null) nodeGreater.parent = grandParent;
                }
            }
        }
    }
}

 

后缀树  

package com.jwetherell.algorithms.data_structures;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;


/**
 * A suffix tree is a data structure that presents the suffixes of a given
 * string in a way that allows for a particularly fast implementation of many
 * important string operations. This implementation is based on the Ukkonen's 
 * algorithm.
 * 
 * http://en.wikipedia.org/wiki/Suffix_tree
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */
public class SuffixTree<C extends CharSequence> {

    private static final char DEFAULT_END_SEQ_CHAR = '$';

    private String string = null;
    private char[] characters = null;

    private Map<Integer, Link> linksMap = new HashMap<Integer, Link>();
    private Map<Integer, Edge<C>> edgeMap = new TreeMap<Integer, Edge<C>>();

    private int currentNode = 0;
    private int firstCharIndex = 0;
    private int lastCharIndex = -1;

    private char END_SEQ_CHAR = DEFAULT_END_SEQ_CHAR;


    /**
     * Create suffix tree with sequence and default end sequence.
     * 
     * @param seq to create a suffix tree with.
     */
    public SuffixTree(C seq) {
        this(seq, DEFAULT_END_SEQ_CHAR);
    }

    /**
     * Create suffix tree with sequence and end sequence parameter.
     * 
     * @param seq to create a suffix tree with.
     * @param endSeq which defines the end of a sequence.
     */
    public SuffixTree(C seq, char endSeq) {
        END_SEQ_CHAR = endSeq;
        StringBuilder builder = new StringBuilder(seq);
        if (builder.indexOf(String.valueOf(seq))>=0) builder.append(END_SEQ_CHAR);
        string = builder.toString();
        int length = string.length();
        characters = new char[length];
        for (int i = 0; i < length; i++) {
            characters[i] = string.charAt(i);
        }

        for (int i = 0; i < length; i++) {
            addPrefix(i);
        }
    }

    /**
     * Does the sub-sequence exist in the suffix tree.
     * 
     * @param sub sub-sequence to locate in the tree.
     * @return True if the sub-sequence exist in the tree.
     */
    public boolean doesSubStringExist(C sub) {
        char[] chars = new char[sub.length()];
        for (int i = 0; i < sub.length(); i++) {
            chars[i] = sub.charAt(i);
        }
        int[] indices = searchEdges(chars);
        int start = indices[0];
        int end = indices[1];
        int length = end - start;
        if (length == (chars.length - 1)) return true;
        return false;
    }

    /**
     * Get all the suffixes in the tree.
     * 
     * @return set of suffixes in the tree.
     */
    public Set<String> getSuffixes() {
        Set<String> set = getSuffixes(0);
        return set;
    }

    /**
     * Get all suffixes at starting node.
     * 
     * @param start node.
     * @return set of suffixes in the tree at start node.
     */
    private Set<String> getSuffixes(int start) {
        Set<String> set = new TreeSet<String>();
        for (int key : edgeMap.keySet()) {
            Edge<C> e = edgeMap.get(key);
            if (e == null) continue;
            if (e.startNode != start) continue;

            String s = (string.substring(e.firstCharIndex, e.lastCharIndex + 1));
            Link n = linksMap.get(e.endNode);
            if (n == null) {
                int index = s.indexOf(END_SEQ_CHAR);
                if (index>=0) s = s.substring(0, index);
                set.add(s);
            } else {
                Set<String> set2 = getSuffixes(e.endNode);
                for (String s2 : set2) {
                    int index = s2.indexOf(END_SEQ_CHAR);
                    if (index>=0) s2 = s2.substring(0, index);
                    set.add(s + s2);
                }
            }
        }
        return set;
    }

    /**
     * Get all edges in the table
     * 
     * @return debug string.
     */
    public String getEdgesTable() {
        StringBuilder builder = new StringBuilder();
        if (edgeMap.size() > 0) {
            int lastCharIndex = characters.length;
            builder.append("Edge\tStart\tEnd\tSuf\tfirst\tlast\tString\n");
            for (int key : edgeMap.keySet()) {
                Edge<C> e = edgeMap.get(key);
                Link link = linksMap.get(e.endNode);
                int suffix = (link != null) ? link.suffixNode : -1;
                builder.append("\t" + e.startNode + "\t" + e.endNode + "\t" + suffix + "\t" + e.firstCharIndex + "\t" + e.lastCharIndex + "\t");
                int begin = e.firstCharIndex;
                int end = (lastCharIndex < e.lastCharIndex) ? lastCharIndex : e.lastCharIndex;
                builder.append(string.substring(begin, end + 1));
                builder.append("\n");
            }
            builder.append("Link\tStart\tEnd\n");
            for (int key : linksMap.keySet()) {
                Link link = linksMap.get(key);
                builder.append("\t" + link.node + "\t" + link.suffixNode + "\n");
            }
        }
        return builder.toString();
    }

    /**
     * Add prefix at index.
     * 
     * @param index to add prefix at.
     */
    private void addPrefix(int index) {
        int parentNodeIndex = 0;
        int lastParentIndex = -1;

        while (true) {
            Edge<C> edge = null;
            parentNodeIndex = currentNode;
            if (isExplicit()) {
                edge = Edge.find(this, currentNode, characters[index]);
                if (edge != null) {
                    // Edge already exists
                    break;
                }
            } else {
                // Implicit node, a little more complicated
                edge = Edge.find(this, currentNode, characters[firstCharIndex]);
                int span = lastCharIndex - firstCharIndex;
                if (characters[edge.firstCharIndex + span + 1] == characters[index]) {
                    // If the edge is the last char, don't split
                    break;
                }
                parentNodeIndex = edge.split(currentNode, firstCharIndex, lastCharIndex);
            }
            edge = new Edge<C>(this, index, characters.length - 1, parentNodeIndex);
            if (lastParentIndex > 0) {
                // Last parent is not root, create a link.
                linksMap.get(lastParentIndex).suffixNode = parentNodeIndex;
            }
            lastParentIndex = parentNodeIndex;
            if (currentNode == 0) {
                firstCharIndex++;
            } else {
                // Current node is not root, follow link
                currentNode = linksMap.get(currentNode).suffixNode;
            }
            if (!isExplicit()) canonize();
        }
        if (lastParentIndex > 0) {
            // Last parent is not root, create a link.
            linksMap.get(lastParentIndex).suffixNode = parentNodeIndex;
        }
        lastParentIndex = parentNodeIndex;
        lastCharIndex++; // Now the endpoint is the next active point
        if (!isExplicit()) canonize();
    };

    /**
     * Is the tree explicit
     * 
     * @return True if explicit.
     */
    private boolean isExplicit() {
        return firstCharIndex > lastCharIndex;
    }

    /**
     * Canonize the tree.
     */
    private void canonize() {
        Edge<C> edge = Edge.find(this, currentNode, characters[firstCharIndex]);
        int edgeSpan = edge.lastCharIndex - edge.firstCharIndex;
        while (edgeSpan <= (lastCharIndex - firstCharIndex)) {
            firstCharIndex = firstCharIndex + edgeSpan + 1;
            currentNode = edge.endNode;
            if (firstCharIndex <= lastCharIndex) {
                edge = Edge.find(this, edge.endNode, characters[firstCharIndex]);
                edgeSpan = edge.lastCharIndex - edge.firstCharIndex;
            }
        }
    }

    /**
     * Returns a two element int array who's 0th index is the start index and
     * 1th is the end index.
     */
    private int[] searchEdges(char[] query) {
        int startNode = 0;
        int queryPosition = 0;
        int startIndex = -1;
        int endIndex = -1;
        boolean stop = false;

        while (!stop && queryPosition < query.length) {
            Edge<C> edge = Edge.find(this, startNode, query[queryPosition]);
            if (edge == null) {
                stop = true;
                break;
            }
            if (startNode == 0) startIndex = edge.firstCharIndex;
            for (int i = edge.firstCharIndex; i <= edge.lastCharIndex; i++) {
                if (queryPosition >= query.length) {
                    stop = true;
                    break;
                } else if (query[queryPosition] == characters[i]) {
                    queryPosition++;
                    endIndex = i;
                } else {
                    stop = true;
                    break;
                }
            }
            if (!stop) { // proceed with next node
                startNode = edge.endNode;
                if (startNode == -1) stop = true;
            }
        }
        return (new int[] { startIndex, endIndex });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("String = ").append(this.string).append("\n");
        builder.append("End of word character = ").append(END_SEQ_CHAR).append("\n");
        builder.append(TreePrinter.getString(this));
        return builder.toString();
    }


    private static class Link implements Comparable<Link> {

        private int node = 0;
        private int suffixNode = -1;


        public Link(int node) {
            this.node = node;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("node=").append(node).append("\n");
            builder.append("suffixNode=").append(suffixNode).append("\n");
            return builder.toString();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(Link link) {
            if (link == null) return -1;

            if (node < link.node) return -1;
            if (node > link.node) return 1;

            if (suffixNode < link.suffixNode) return -1;
            if (suffixNode > link.suffixNode) return 1;

            return 0;
        }
    };

    private static class Edge<C extends CharSequence> implements Comparable<Edge<C>> {

        private static final int KEY_MOD = 2179; // Should be a prime that is
                                                 // roughly 10% larger than the
                                                 // String
        private static int count = 1;

        private SuffixTree<C> tree = null;

        private int startNode = -1;
        private int endNode = 0;
        private int firstCharIndex = 0;
        private int lastCharIndex = 0;

        private Edge(SuffixTree<C> tree, int first, int last, int parent) {
            this.tree = tree;
            firstCharIndex = first;
            lastCharIndex = last;
            startNode = parent;
            endNode = count++;
            insert(this);
        }

        private int getKey() {
            return key(startNode, tree.characters[firstCharIndex]);
        }

        private static int key(int node, char c) {
            return ((node << 8) + c) % KEY_MOD;
        }

        private void insert(Edge<C> edge) {
            tree.edgeMap.put(edge.getKey(), edge);
        }

        private void remove(Edge<C> edge) {
            int i = edge.getKey();
            Edge<C> e = tree.edgeMap.remove(i);
            while (true) {
                e.startNode = -1;
                int j = i;
                while (true) {
                    i = ++i % KEY_MOD;
                    e = tree.edgeMap.get(i);
                    if (e == null) return;
                    int r = key(e.startNode, tree.characters[e.firstCharIndex]);
                    if (i >= r && r > j) continue;
                    if (r > j && j > i) continue;
                    if (j > i && i >= r) continue;
                    break;
                }
                tree.edgeMap.put(j, e);
            }
        }

        private static <C extends CharSequence> Edge<C> find(SuffixTree<C> tree, int node, char c) {
            int key = key(node, c);
            return tree.edgeMap.get(key);
        }

        private int split(int originNode, int firstCharIndex, int lastCharIndex) {
            remove(this);
            Edge<C> newEdge = new Edge<C>(tree, this.firstCharIndex, this.firstCharIndex + lastCharIndex - firstCharIndex, originNode);
            Link link = tree.linksMap.get(newEdge.endNode);
            if (link == null) {
                link = new Link(newEdge.endNode);
                tree.linksMap.put(newEdge.endNode, link);
            }
            tree.linksMap.get(newEdge.endNode).suffixNode = originNode;
            this.firstCharIndex += lastCharIndex - firstCharIndex + 1;
            this.startNode = newEdge.endNode;
            insert(this);
            return newEdge.endNode;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int hashCode() {
            return getKey();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (obj instanceof Edge) return false;

            @SuppressWarnings("unchecked")
            Edge<C> e = (Edge<C>) obj;
            if (startNode == e.startNode && tree.characters[firstCharIndex] == tree.characters[e.firstCharIndex]) {
                return true;
            }

            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("startNode=").append(startNode).append("\n");
            builder.append("endNode=").append(endNode).append("\n");
            builder.append("firstCharIndex=").append(firstCharIndex).append("\n");
            builder.append("lastCharIndex=").append(lastCharIndex).append("\n");
            String s = tree.string.substring(firstCharIndex, lastCharIndex + 1);
            builder.append("string=").append(s).append("\n");
            return builder.toString();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compareTo(Edge<C> edge) {
            if (edge == null) return -1;

            if (startNode < edge.startNode) return -1;
            if (startNode > edge.startNode) return 1;

            if (endNode < edge.endNode) return -1;
            if (endNode > edge.endNode) return 1;

            if (firstCharIndex < edge.firstCharIndex) return -1;
            if (firstCharIndex > edge.firstCharIndex) return 1;

            if (lastCharIndex < edge.lastCharIndex) return -1;
            if (lastCharIndex > edge.lastCharIndex) return 1;

            return 0;
        }
    }

    protected static class TreePrinter {

        public static <C extends CharSequence> void printNode(SuffixTree<C> tree) {
            System.out.println(getString(tree, null, "", true));
        }

        public static <C extends CharSequence> String getString(SuffixTree<C> tree) {
            return getString(tree, null, "", true);
        }

        private static <C extends CharSequence> String getString(SuffixTree<C> tree, Edge<C> e, String prefix, boolean isTail) {
            StringBuilder builder = new StringBuilder();
            int value = 0;
            if (e != null) {
                value = e.endNode;
                String string = tree.string.substring(e.firstCharIndex, e.lastCharIndex + 1);
                int index = string.indexOf(tree.END_SEQ_CHAR);
                if (index>=0) string = string.substring(0, index+1);
                builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + value + ") " + string + "\n");
            } else {
                builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + 0 + ")" + "\n");
            }

            if (tree.edgeMap.size() > 0) {
                List<Edge<C>> children = new LinkedList<Edge<C>>();
                for (Edge<C> edge : tree.edgeMap.values()) {
                    if (edge != null && (edge.startNode == value)) {
                        children.add(edge);
                    }
                }
                if (children.size()>0) {
                    for (int i = 0; i < children.size() - 1; i++) {
                        Edge<C> edge = children.get(i);
                        builder.append(getString(tree, edge, prefix + (isTail ? "    " : "│   "), false));
                    }
                    if (children.size() >= 1) {
                        Edge<C> edge = children.get(children.size() - 1);
                        builder.append(getString(tree, edge, prefix + (isTail ? "    " : "│   "), true));
                    }
                }
            }
            return builder.toString();
        }
    }
}
 

测试代码

private static boolean testIntervalTree() {
        {
            //Interval tree
            if (debug>1) System.out.println("Interval Tree.");
            java.util.List<IntervalTree.IntervalData<String>> intervals = new ArrayList<IntervalTree.IntervalData<String>>();
            intervals.add((new IntervalTree.IntervalData<String>(2, 6, "RED")));
            intervals.add((new IntervalTree.IntervalData<String>(3, 5, "ORANGE")));
            intervals.add((new IntervalTree.IntervalData<String>(4, 11, "GREEN")));
            intervals.add((new IntervalTree.IntervalData<String>(5, 10, "DARK_GREEN")));
            intervals.add((new IntervalTree.IntervalData<String>(8, 12, "BLUE")));
            intervals.add((new IntervalTree.IntervalData<String>(9, 14, "PURPLE")));
            intervals.add((new IntervalTree.IntervalData<String>(13, 15, "BLACK")));
            IntervalTree<String> tree = new IntervalTree<String>(intervals);
            if (debug>1) System.out.println(tree);

            IntervalTree.IntervalData<String> query = tree.query(2);
            if (debug>1) System.out.println("2: "+query);

            query = tree.query(4); //Stabbing query
            if (debug>1) System.out.println("4: "+query);

            query = tree.query(9); //Stabbing query
            if (debug>1) System.out.println("9: "+query);

            query = tree.query(1, 16); //Range query
            if (debug>1) System.out.println("1->16: "+query);

            query = tree.query(7, 14); //Range query
            if (debug>1) System.out.println("7->14: "+query);

            query = tree.query(14, 15); //Range query
            if (debug>1) System.out.println("14->15: "+query);

            if (debug>1) System.out.println();
        }

        {
            //Lifespan Interval tree
            if (debug>1) System.out.println("Lifespan Interval Tree.");
            java.util.List<IntervalTree.IntervalData<String>> intervals = new ArrayList<IntervalTree.IntervalData<String>>();
            intervals.add((new IntervalTree.IntervalData<String>(1888, 1971, "Stravinsky")));
            intervals.add((new IntervalTree.IntervalData<String>(1874, 1951, "Schoenberg")));
            intervals.add((new IntervalTree.IntervalData<String>(1843, 1907, "Grieg")));
            intervals.add((new IntervalTree.IntervalData<String>(1779, 1828, "Schubert")));
            intervals.add((new IntervalTree.IntervalData<String>(1756, 1791, "Mozart")));
            intervals.add((new IntervalTree.IntervalData<String>(1585, 1672, "Schuetz")));
            IntervalTree<String> tree = new IntervalTree<String>(intervals);
            if (debug>1) System.out.println(tree);

            IntervalTree.IntervalData<String> query = tree.query(1890);
            if (debug>1) System.out.println("1890: "+query);

            query = tree.query(1909); //Stabbing query
            if (debug>1) System.out.println("1909: "+query);

            query = tree.query(1792, 1903); //Range query
            if (debug>1) System.out.println("1792->1903: "+query);

            query = tree.query(1776, 1799); //Range query
            if (debug>1) System.out.println("1776->1799: "+query);

            if (debug>1) System.out.println();
        }

        return true;
    }
  private static boolean testJavaRedBlackTree() {
        {
            long count = 0;

            long addTime = 0L;
            long removeTime = 0L;
            long beforeAddTime = 0L;
            long afterAddTime = 0L;
            long beforeRemoveTime = 0L;
            long afterRemoveTime = 0L;

            long memory = 0L;
            long beforeMemory = 0L;
            long afterMemory = 0L;

            //Java's Red-Black Tree
            if (debug>1) System.out.println("Java's Red-Black Tree");
            testNames[testIndex] = "Java's RedBlack Tree";

            count++;

            if (debugMemory) beforeMemory = DataStructures.getMemoryUse();
            if (debugTime) beforeAddTime = System.currentTimeMillis();
            java.util.TreeSet<Integer> tree = new java.util.TreeSet<Integer>();
            for (int i=0; i<unsorted.length; i++) {
                int item = unsorted[i];
                tree.add(item);
            }
            if (debugTime) {
                afterAddTime = System.currentTimeMillis();
                addTime += afterAddTime-beforeAddTime;
                if (debug>0) System.out.println("Java's Red-Black Tree add time = "+addTime/count+" ms");
            }
            if (debugMemory) {
                afterMemory = DataStructures.getMemoryUse();
                memory += afterMemory-beforeMemory;
                if (debug>0) System.out.println("Java's Red-Black Tree memory use = "+(memory/count)+" bytes");
            }

            boolean contains = tree.contains(INVALID);
            boolean removed = tree.remove(INVALID);
            if (contains || removed) {
                System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);

            if (debug>1) System.out.println(tree.toString());

            long lookupTime = 0L;
            long beforeLookupTime = 0L;
            long afterLookupTime = 0L;
            if (debugTime) beforeLookupTime = System.currentTimeMillis();
            for (int item : unsorted) {
                tree.contains(item);
            }
            if (debugTime) {
                afterLookupTime = System.currentTimeMillis();
                lookupTime += afterLookupTime-beforeLookupTime;
                if (debug>0) System.out.println("Java's Red-Black lookup time = "+lookupTime/count+" ms");
            }

            if (debugTime) beforeRemoveTime = System.currentTimeMillis();
            for (int i=0; i<unsorted.length; i++) {
                int item = unsorted[i];
                tree.remove(item);
            }
            if (debugTime) {
                afterRemoveTime = System.currentTimeMillis();
                removeTime += afterRemoveTime-beforeRemoveTime;
                if (debug>0) System.out.println("Java's Red-Black Tree remove time = "+removeTime/count+" ms");
            }

            contains = tree.contains(INVALID);
            removed = tree.remove(INVALID);
            if (contains || removed) {
                System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);

            count++;

            if (debugMemory) beforeMemory = DataStructures.getMemoryUse();
            if (debugTime) beforeAddTime = System.currentTimeMillis();
            for (int i=unsorted.length-1; i>=0; i--) {
                int item = unsorted[i];
                tree.add(item);
            }
            if (debugTime) {
                afterAddTime = System.currentTimeMillis();
                addTime += afterAddTime-beforeAddTime;
                if (debug>0) System.out.println("Java's Red-Black Tree add time = "+addTime/count+" ms");
            }
            if (debugMemory) {
                afterMemory = DataStructures.getMemoryUse();
                memory += afterMemory-beforeMemory;
                if (debug>0) System.out.println("Java's Red-Black Tree memory use = "+(memory/count)+" bytes");
            }

            contains = tree.contains(INVALID);
            removed = tree.remove(INVALID);
            if (contains || removed) {
                System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);

            if (debug>1) System.out.println(tree.toString());

            lookupTime = 0L;
            beforeLookupTime = 0L;
            afterLookupTime = 0L;
            if (debugTime) beforeLookupTime = System.currentTimeMillis();
            for (int item : unsorted) {
                tree.contains(item);
            }
            if (debugTime) {
                afterLookupTime = System.currentTimeMillis();
                lookupTime += afterLookupTime-beforeLookupTime;
                if (debug>0) System.out.println("Java's Red-Black Tree lookup time = "+lookupTime/count+" ms");
            }

            if (debugTime) beforeRemoveTime = System.currentTimeMillis();
            for (int i=0; i<unsorted.length; i++) {
                int item = unsorted[i];
                tree.remove(item);
            }
            if (debugTime) {
                afterRemoveTime = System.currentTimeMillis();
                removeTime += afterRemoveTime-beforeRemoveTime;
                if (debug>0) System.out.println("Java's Red-Black Tree remove time = "+removeTime/count+" ms");
            }

            contains = tree.contains(INVALID);
            removed = tree.remove(INVALID);
            if (contains || removed) {
                System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);

            //sorted
            long addSortedTime = 0L;
            long removeSortedTime = 0L;
            long beforeAddSortedTime = 0L;
            long afterAddSortedTime = 0L;
            long beforeRemoveSortedTime = 0L;
            long afterRemoveSortedTime = 0L;

            if (debugMemory) beforeMemory = DataStructures.getMemoryUse();
            if (debugTime) beforeAddSortedTime = System.currentTimeMillis();
            for (int i=0; i<sorted.length; i++) {
                int item = sorted[i];
                tree.add(item);
            }
            if (debugTime) {
                afterAddSortedTime = System.currentTimeMillis();
                addSortedTime += afterAddSortedTime-beforeAddSortedTime;
                if (debug>0) System.out.println("Java's Red-Black Tree add time = "+addSortedTime+" ms");
            }
            if (debugMemory) {
                afterMemory = DataStructures.getMemoryUse();
                memory += afterMemory-beforeMemory;
                if (debug>0) System.out.println("Java's Red-Black Tree memory use = "+(memory/(count+1))+" bytes");
            }

            contains = tree.contains(INVALID);
            removed = tree.remove(INVALID);
            if (contains || removed) {
                System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);

            if (debug>1) System.out.println(tree.toString());

            lookupTime = 0L;
            beforeLookupTime = 0L;
            afterLookupTime = 0L;
            if (debugTime) beforeLookupTime = System.currentTimeMillis();
            for (int item : sorted) {
                tree.contains(item);
            }
            if (debugTime) {
                afterLookupTime = System.currentTimeMillis();
                lookupTime += afterLookupTime-beforeLookupTime;
                if (debug>0) System.out.println("Java's Red-Black Tree lookup time = "+lookupTime/(count+1)+" ms");
            }

            if (debugTime) beforeRemoveSortedTime = System.currentTimeMillis();
            for (int i=sorted.length-1; i>=0; i--) {
                int item = sorted[i];
                tree.remove(item);
            }
            if (debugTime) {
                afterRemoveSortedTime = System.currentTimeMillis();
                removeSortedTime += afterRemoveSortedTime-beforeRemoveSortedTime;
                if (debug>0) System.out.println("Java's Red-Black Tree remove time = "+removeSortedTime+" ms");
            }

            contains = tree.contains(INVALID);
            removed = tree.remove(INVALID);
            if (contains || removed) {
                System.err.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Java's Red-Black Tree invalidity check. contains="+contains+" removed="+removed);

            if (testResults[testIndex]==null) testResults[testIndex] = new long[6];
            testResults[testIndex][0]+=addTime/count;
            testResults[testIndex][1]+=removeTime/count;
            testResults[testIndex][2]+=addSortedTime;
            testResults[testIndex][3]+=removeSortedTime;
            testResults[testIndex][4]+=lookupTime/(count+1);
            testResults[testIndex++][5]+=memory/(count+1);

            if (debug>1) System.out.println();
        }
        
        return true;
    }
     private static boolean testKdTree() {
        {
            // K-D TREE
            if (debug>1) System.out.println("k-d tree with node.");

            java.util.List<KdTree.XYZPoint> points = new ArrayList<KdTree.XYZPoint>();
            KdTree.XYZPoint p1 = new KdTree.XYZPoint(2,3);
            points.add(p1);
            KdTree.XYZPoint p2 = new KdTree.XYZPoint(5,4);
            points.add(p2);
            KdTree.XYZPoint p3 = new KdTree.XYZPoint(9,6);
            points.add(p3);
            KdTree.XYZPoint p4 = new KdTree.XYZPoint(4,7);
            points.add(p4);
            KdTree.XYZPoint p5 = new KdTree.XYZPoint(8,1);
            points.add(p5);
            KdTree.XYZPoint p6 = new KdTree.XYZPoint(7,2);
            points.add(p6);
            KdTree<KdTree.XYZPoint> kdTree = new KdTree<KdTree.XYZPoint>(points);
            if (debug>1) System.out.println(kdTree.toString());

            Collection<KdTree.XYZPoint> result = kdTree.nearestNeighbourSearch(1,p3);
            if (debug>1) System.out.println("NNS for "+p3+" result="+result+"\n");

            KdTree.XYZPoint search = new KdTree.XYZPoint(1,4);
            result = kdTree.nearestNeighbourSearch(4,search);
            if (debug>1) System.out.println("NNS for "+search+" result="+result+"\n");

            kdTree.remove(p6);
            if (debug>1) System.out.println("Removed "+p6+"\n"+kdTree.toString());
            kdTree.remove(p4);
            if (debug>1) System.out.println("Removed "+p4+"\n"+kdTree.toString());
            kdTree.remove(p3);
            if (debug>1) System.out.println("Removed "+p3+"\n"+kdTree.toString());
            kdTree.remove(p5);
            if (debug>1) System.out.println("Removed "+p5+"\n"+kdTree.toString());
            kdTree.remove(p1);
            if (debug>1) System.out.println("Removed "+p1+"\n"+kdTree.toString());
            kdTree.remove(p2);
            if (debug>1) System.out.println("Removed "+p2+"\n"+kdTree.toString());

            if (debug>1) System.out.println();
        }

        return true;
    } private static boolean testSegmentTree() {

        {
            //Quadrant Segment tree
            if (debug>1) System.out.println("Quadrant Segment Tree.");
            java.util.List<SegmentTree.Data.QuadrantData> segments = new ArrayList<SegmentTree.Data.QuadrantData>();
            segments.add(new SegmentTree.Data.QuadrantData(0,  1, 0, 0, 0)); //first point in the 0th quadrant
            segments.add(new SegmentTree.Data.QuadrantData(1,  0, 1, 0, 0)); //second point in the 1st quadrant
            segments.add(new SegmentTree.Data.QuadrantData(2,  0, 0, 1, 0)); //third point in the 2nd quadrant
            segments.add(new SegmentTree.Data.QuadrantData(3,  0, 0, 0, 1)); //fourth point in the 3rd quadrant
            FlatSegmentTree<SegmentTree.Data.QuadrantData> tree = new FlatSegmentTree<SegmentTree.Data.QuadrantData>(segments);
            if (debug>1) System.out.println(tree);

            SegmentTree.Data.QuadrantData query = tree.query(0, 3);
            if (debug>1) System.out.println("0->3: "+query+"\n");

            query = tree.query(2, 3);
            if (debug>1) System.out.println("2->3: "+query+"\n");

            query = tree.query(0, 2);
            if (debug>1) System.out.println("0->2: "+query+"\n");

            if (debug>1) System.out.println();
        }

        {
            //Range Maximum Segment tree
            if (debug>1) System.out.println("Range Maximum Segment Tree.");
            java.util.List<SegmentTree.Data.RangeMaximumData<Integer>> segments = new ArrayList<SegmentTree.Data.RangeMaximumData<Integer>>();
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(0, (Integer)4));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(1, (Integer)2));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(2, (Integer)6));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(3, (Integer)3));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(4, (Integer)1));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(5, (Integer)5));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(6, (Integer)0));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(7, 17, (Integer)7));
            segments.add(new SegmentTree.Data.RangeMaximumData<Integer>(21, (Integer)10));
            FlatSegmentTree<SegmentTree.Data.RangeMaximumData<Integer>> tree = new FlatSegmentTree<SegmentTree.Data.RangeMaximumData<Integer>>(segments,3);
            if (debug>1) System.out.println(tree);

            SegmentTree.Data.RangeMaximumData<Integer> query = tree.query(0, 7);
            if (debug>1) System.out.println("0->7: "+query+"\n");

            query = tree.query(0, 21);
            if (debug>1) System.out.println("0->21: "+query+"\n");

            query = tree.query(2, 5);
            if (debug>1) System.out.println("2->5: "+query+"\n");

            query = tree.query(7);
            if (debug>1) System.out.println("7: "+query+"\n");

            if (debug>1) System.out.println();
        }

        {
            //Range Minimum Segment tree
            if (debug>1) System.out.println("Range Minimum Segment Tree.");
            java.util.List<SegmentTree.Data.RangeMinimumData<Integer>> segments = new ArrayList<SegmentTree.Data.RangeMinimumData<Integer>>();
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(0, (Integer)4));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(1, (Integer)2));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(2, (Integer)6));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(3, (Integer)3));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(4, (Integer)1));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(5, (Integer)5));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(6, (Integer)0));
            segments.add(new SegmentTree.Data.RangeMinimumData<Integer>(17, (Integer)7));
            FlatSegmentTree<SegmentTree.Data.RangeMinimumData<Integer>> tree = new FlatSegmentTree<SegmentTree.Data.RangeMinimumData<Integer>>(segments,5);
            if (debug>1) System.out.println(tree);

            SegmentTree.Data.RangeMinimumData<Integer> query = tree.query(0, 7);
            if (debug>1) System.out.println("0->7: "+query+"\n");

            query = tree.query(0, 17);
            if (debug>1) System.out.println("0->17: "+query+"\n");

            query = tree.query(1, 3);
            if (debug>1) System.out.println("1->3: "+query+"\n");

            query = tree.query(7);
            if (debug>1) System.out.println("7: "+query+"\n");

            if (debug>1) System.out.println();
        }

        {
            //Range Sum Segment tree
            if (debug>1) System.out.println("Range Sum Segment Tree.");
            java.util.List<SegmentTree.Data.RangeSumData<Integer>> segments = new ArrayList<SegmentTree.Data.RangeSumData<Integer>>();
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(0, (Integer)4));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(1, (Integer)2));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(2, (Integer)6));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(3, (Integer)3));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(4, (Integer)1));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(5, (Integer)5));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(6, (Integer)0));
            segments.add(new SegmentTree.Data.RangeSumData<Integer>(17, (Integer)7));
            FlatSegmentTree<SegmentTree.Data.RangeSumData<Integer>> tree = new FlatSegmentTree<SegmentTree.Data.RangeSumData<Integer>>(segments,10);
            if (debug>1) System.out.println(tree);

            SegmentTree.Data.RangeSumData<Integer> query = tree.query(0, 8);
            if (debug>1) System.out.println("0->8: "+query+"\n");

            query = tree.query(0, 17);
            if (debug>1) System.out.println("0->17: "+query+"\n");

            query = tree.query(2, 5);
            if (debug>1) System.out.println("2->5: "+query+"\n");

            query = tree.query(10, 17);
            if (debug>1) System.out.println("10->17: "+query+"\n");

            query = tree.query(16);
            if (debug>1) System.out.println("16: "+query+"\n");

            query = tree.query(17);
            if (debug>1) System.out.println("17: "+query+"\n");

            if (debug>1) System.out.println();
        }

        {
            //Interval Segment tree
            if (debug>1) System.out.println("Interval Segment Tree.");
            java.util.List<SegmentTree.Data.IntervalData<String>> segments = new ArrayList<SegmentTree.Data.IntervalData<String>>();
            segments.add((new SegmentTree.Data.IntervalData<String>(2, 6, "RED")));
            segments.add((new SegmentTree.Data.IntervalData<String>(3, 5, "ORANGE")));
            segments.add((new SegmentTree.Data.IntervalData<String>(4, 11, "GREEN")));
            segments.add((new SegmentTree.Data.IntervalData<String>(5, 10, "DARK_GREEN")));
            segments.add((new SegmentTree.Data.IntervalData<String>(8, 12, "BLUE")));
            segments.add((new SegmentTree.Data.IntervalData<String>(9, 14, "PURPLE")));
            segments.add((new SegmentTree.Data.IntervalData<String>(13, 15, "BLACK")));
            DynamicSegmentTree<SegmentTree.Data.IntervalData<String>> tree = new DynamicSegmentTree<SegmentTree.Data.IntervalData<String>>(segments);
            if (debug>1) System.out.println(tree);

            SegmentTree.Data.IntervalData<String> query = tree.query(2);
            if (debug>1) System.out.println("2: "+query);

            query = tree.query(4); //Stabbing query
            if (debug>1) System.out.println("4: "+query);

            query = tree.query(9); //Stabbing query
            if (debug>1) System.out.println("9: "+query);

            query = tree.query(1, 16); //Range query
            if (debug>1) System.out.println("1->16: "+query);

            query = tree.query(7, 14); //Range query
            if (debug>1) System.out.println("7->14: "+query);

            query = tree.query(14, 15); //Range query
            if (debug>1) System.out.println("14->15: "+query);

            if (debug>1) System.out.println();
        }

        {
            //Lifespan Interval Segment tree
            if (debug>1) System.out.println("Lifespan Interval Segment Tree.");
            java.util.List<SegmentTree.Data.IntervalData<String>> segments = new ArrayList<SegmentTree.Data.IntervalData<String>>();
            segments.add((new SegmentTree.Data.IntervalData<String>(1888, 1971, "Stravinsky")));
            segments.add((new SegmentTree.Data.IntervalData<String>(1874, 1951, "Schoenberg")));
            segments.add((new SegmentTree.Data.IntervalData<String>(1843, 1907, "Grieg")));
            segments.add((new SegmentTree.Data.IntervalData<String>(1779, 1828, "Schubert")));
            segments.add((new SegmentTree.Data.IntervalData<String>(1756, 1791, "Mozart")));
            segments.add((new SegmentTree.Data.IntervalData<String>(1585, 1672, "Schuetz")));
            DynamicSegmentTree<SegmentTree.Data.IntervalData<String>> tree = new DynamicSegmentTree<SegmentTree.Data.IntervalData<String>>(segments,25);
            if (debug>1) System.out.println(tree);

            SegmentTree.Data.IntervalData<String> query = tree.query(1890);
            if (debug>1) System.out.println("1890: "+query);

            query = tree.query(1909); //Stabbing query
            if (debug>1) System.out.println("1909: "+query);

            query = tree.query(1792, 1903); //Range query
            if (debug>1) System.out.println("1792->1903: "+query);

            query = tree.query(1776, 1799); //Range query
            if (debug>1) System.out.println("1776->1799: "+query);

            if (debug>1) System.out.println();
        }

        return true;
    } private static boolean testSplayTree() {
        {
            long count = 0;

            long addTime = 0L;
            long removeTime = 0L;
            long beforeAddTime = 0L;
            long afterAddTime = 0L;
            long beforeRemoveTime = 0L;
            long afterRemoveTime = 0L;

            long memory = 0L;
            long beforeMemory = 0L;
            long afterMemory = 0L;

            //Splay Tree
            if (debug>1) System.out.println("Splay Tree.");
            testNames[testIndex] = "Splay Tree";

            count++;

            if (debugMemory) beforeMemory = DataStructures.getMemoryUse();
            if (debugTime) beforeAddTime = System.currentTimeMillis();
            SplayTree<Integer> splay = new SplayTree<Integer>();
            for (int i=0; i<unsorted.length; i++) {
                int item = unsorted[i];
                splay.add(item);
                if (validateStructure && !(splay.size()==(i+1))) {
                    System.err.println("YIKES!! "+item+" caused a size mismatch.");
                    handleError(splay);
                    return false;
                }
                if (validateContents && !splay.contains(item)) {
                    System.err.println("YIKES!! "+item+" doesn't exists.");
                    handleError(splay);
                    return false;
                }
            }
            if (debugTime) {
                afterAddTime = System.currentTimeMillis();
                addTime += afterAddTime-beforeAddTime;
                if (debug>0) System.out.println("Splay Tree add time = "+addTime/count+" ms");
            }
            if (debugMemory) {
                afterMemory = DataStructures.getMemoryUse();
                memory += afterMemory-beforeMemory;
                if (debug>0) System.out.println("Splay Tree memory use = "+(memory/count)+" bytes");
            }

            boolean contains = splay.contains(INVALID);
            boolean removed = splay.remove(INVALID);
            if (contains || removed) {
                System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);

            if (debug>1) System.out.println(splay.toString());

            long lookupTime = 0L;
            long beforeLookupTime = 0L;
            long afterLookupTime = 0L;
            if (debugTime) beforeLookupTime = System.currentTimeMillis();
            for (int item : unsorted) {
                splay.contains(item);
            }
            if (debugTime) {
                afterLookupTime = System.currentTimeMillis();
                lookupTime += afterLookupTime-beforeLookupTime;
                if (debug>0) System.out.println("Splay Tree lookup time = "+lookupTime/count+" ms");
            }

            if (debugTime) beforeRemoveTime = System.currentTimeMillis();
            for (int i=0; i<unsorted.length; i++) {
                int item = unsorted[i];
                splay.remove(item);
                if (validateStructure && !(splay.size()==((unsorted.length-1)-i))) {
                    System.err.println("YIKES!! "+item+" caused a size mismatch.");
                    handleError(splay);
                    return false;
                }
                if (validateContents && splay.contains(item)) {
                    System.err.println("YIKES!! "+item+" still exists.");
                    handleError(splay);
                    return false;
                }
            }
            if (debugTime) {
                afterRemoveTime = System.currentTimeMillis();
                removeTime += afterRemoveTime-beforeRemoveTime;
                if (debug>0) System.out.println("Splay Tree remove time = "+removeTime/count+" ms");
            }

            contains = splay.contains(INVALID);
            removed = splay.remove(INVALID);
            if (contains || removed) {
                System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);

            count++;

            if (debugMemory) beforeMemory = DataStructures.getMemoryUse();
            if (debugTime) beforeAddTime = System.currentTimeMillis();
            for (int i=unsorted.length-1; i>=0; i--) {
                int item = unsorted[i];
                splay.add(item);
                if (validateStructure && !(splay.size()==(unsorted.length-i))) {
                    System.err.println("YIKES!! "+item+" caused a size mismatch.");
                    handleError(splay);
                    return false;
                }
                if (validateContents && !splay.contains(item)) {
                    System.err.println("YIKES!! "+item+" doesn't exists.");
                    handleError(splay);
                    return false;
                }
            }
            if (debugTime) {
                afterAddTime = System.currentTimeMillis();
                addTime += afterAddTime-beforeAddTime;
                if (debug>0) System.out.println("Splay Tree add time = "+addTime/count+" ms");
            }
            if (debugMemory) {
                afterMemory = DataStructures.getMemoryUse();
                memory += afterMemory-beforeMemory;
                if (debug>0) System.out.println("Splay Tree memory use = "+(memory/count)+" bytes");
            }

            contains = splay.contains(INVALID);
            removed = splay.remove(INVALID);
            if (contains || removed) {
                System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);

            if (debug>1) System.out.println(splay.toString());

            lookupTime = 0L;
            beforeLookupTime = 0L;
            afterLookupTime = 0L;
            if (debugTime) beforeLookupTime = System.currentTimeMillis();
            for (int item : unsorted) {
                splay.contains(item);
            }
            if (debugTime) {
                afterLookupTime = System.currentTimeMillis();
                lookupTime += afterLookupTime-beforeLookupTime;
                if (debug>0) System.out.println("Splay Tree lookup time = "+lookupTime/count+" ms");
            }

            if (debugTime) beforeRemoveTime = System.currentTimeMillis();
            for (int i=0; i<unsorted.length; i++) {
                int item = unsorted[i];
                splay.remove(item);
                if (validateStructure && !(splay.size()==((unsorted.length-1)-i))) {
                    System.err.println("YIKES!! "+item+" caused a size mismatch.");
                    handleError(splay);
                    return false;
                }
                if (validateContents && splay.contains(item)) {
                    System.err.println("YIKES!! "+item+" still exists.");
                    handleError(splay);
                    return false;
                }
            }
            if (debugTime) {
                afterRemoveTime = System.currentTimeMillis();
                removeTime += afterRemoveTime-beforeRemoveTime;
                if (debug>0) System.out.println("Splay Tree remove time = "+removeTime/count+" ms");
            }

            contains = splay.contains(INVALID);
            removed = splay.remove(INVALID);
            if (contains || removed) {
                System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);

            //sorted
            long addSortedTime = 0L;
            long removeSortedTime = 0L;
            long beforeAddSortedTime = 0L;
            long afterAddSortedTime = 0L;
            long beforeRemoveSortedTime = 0L;
            long afterRemoveSortedTime = 0L;

            if (debugMemory) beforeMemory = DataStructures.getMemoryUse();
            if (debugTime) beforeAddSortedTime = System.currentTimeMillis();
            for (int i=0; i<sorted.length; i++) {
                int item = sorted[i];
                splay.add(item);
                if (validateStructure && !(splay.size()==(i+1))) {
                    System.err.println("YIKES!! "+item+" caused a size mismatch.");
                    handleError(splay);
                    return false;
                }
                if (validateContents && !splay.contains(item)) {
                    System.err.println("YIKES!! "+item+" doesn't exist.");
                    handleError(splay);
                    return false;
                }
            }
            if (debugTime) {
                afterAddSortedTime = System.currentTimeMillis();
                addSortedTime += afterAddSortedTime-beforeAddSortedTime;
                if (debug>0) System.out.println("Splay Tree add time = "+addSortedTime+" ms");
            }
            if (debugMemory) {
                afterMemory = DataStructures.getMemoryUse();
                memory += afterMemory-beforeMemory;
                if (debug>0) System.out.println("Splay Tree memory use = "+(memory/(count+1))+" bytes");
            }

            contains = splay.contains(INVALID);
            removed = splay.remove(INVALID);
            if (contains || removed) {
                System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);

            if (debug>1) System.out.println(splay.toString());

            lookupTime = 0L;
            beforeLookupTime = 0L;
            afterLookupTime = 0L;
            if (debugTime) beforeLookupTime = System.currentTimeMillis();
            for (int item : sorted) {
                splay.contains(item);
            }
            if (debugTime) {
                afterLookupTime = System.currentTimeMillis();
                lookupTime += afterLookupTime-beforeLookupTime;
                if (debug>0) System.out.println("Splay Tree lookup time = "+lookupTime/(count+1)+" ms");
            }

            if (debugTime) beforeRemoveSortedTime = System.currentTimeMillis();
            for (int i=sorted.length-1; i>=0; i--) {
                int item = sorted[i];
                splay.remove(item);
                if (validateStructure && !(splay.size()==i)) {
                    System.err.println("YIKES!! "+item+" caused a size mismatch.");
                    handleError(splay);
                    return false;
                }
                if (validateContents && splay.contains(item)) {
                    System.err.println("YIKES!! "+item+" still exists.");
                    handleError(splay);
                    return false;
                }
            }
            if (debugTime) {
                afterRemoveSortedTime = System.currentTimeMillis();
                removeSortedTime += afterRemoveSortedTime-beforeRemoveSortedTime;
                if (debug>0) System.out.println("Splay Tree remove time = "+removeSortedTime+" ms");
            }

            contains = splay.contains(INVALID);
            removed = splay.remove(INVALID);
            if (contains || removed) {
                System.err.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);
                return false;
            } else System.out.println("Splay Tree invalidity check. contains="+contains+" removed="+removed);

            if (testResults[testIndex]==null) testResults[testIndex] = new long[6];
            testResults[testIndex][0]+=addTime/count;
            testResults[testIndex][1]+=removeTime/count;
            testResults[testIndex][2]+=addSortedTime;
            testResults[testIndex][3]+=removeSortedTime;
            testResults[testIndex][4]+=lookupTime/(count+1);
            testResults[testIndex++][5]+=memory/(count+1);

            if (debug>1) System.out.println();
        }
        
        return true;
    }private static boolean testSuffixTree() {
        {
            //Suffix Tree
            if (debug>1) System.out.println("Suffix Tree.");
            String bookkeeper = "bookkeeper";
            SuffixTree<String> tree = new SuffixTree<String>(bookkeeper);
            if (debug>1) System.out.println(tree.toString());
            if (debug>1) System.out.println(tree.getSuffixes());

            boolean exists = tree.doesSubStringExist(bookkeeper);
            if (!exists) {
                System.err.println("YIKES!! "+bookkeeper+" doesn't exists.");
                handleError(tree);
                return false;                
            }
            
            String failed = "booker";
            exists = tree.doesSubStringExist(failed);
            if (exists) {
                System.err.println("YIKES!! "+failed+" exists.");
                handleError(tree);
                return false;                
            }

            String pass = "kkee";
            exists = tree.doesSubStringExist(pass);
            if (!exists) {
                System.err.println("YIKES!! "+pass+" doesn't exists.");
                handleError(tree);
                return false;                
            }

            if (debug>1) System.out.println();
        }
        
        return true;
    }
 
1
3
分享到:
评论

相关推荐

    node-v16.13.1-darwin-arm64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    【前端素材】大数据-大数据可视化系统数据分析通用模版.zip

    大数据技术指的是用于处理和分析大规模数据集的技术和工具。以下是一些常见的大数据技术和工具: Hadoop:Apache Hadoop是一个用于分布式存储和处理大规模数据的开源框架。它包括Hadoop Distributed File System(HDFS)用于数据存储和MapReduce用于数据处理。 Spark:Apache Spark是一个快速、通用的集群计算系统,提供了比MapReduce更快的数据处理能力。它支持内存计算和更多复杂的数据处理流程。 NoSQL数据库:NoSQL数据库(如MongoDB、Cassandra等)则更适用于处理这类数据。 数据仓库:数据仓库是一个用于集成和分析大规模数据的存储系统,一些知名的数据仓库包括Snowflake、Amazon Redshift等。 数据湖:数据湖是一个存储结构化和非结构化数据的存储池,用于支持数据分析和机器学习应用。 机器学习:大数据技术也广泛应用于机器学习领域,支持大规模数据的模型训练和预测分析。 流式处理:针对实时数据处理需求,流式处理技术(如Apache Kafka、Apache Flink)可以实时。

    node-v8.3.0.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    云计算存储架构dr.pptx

    云计算存储架构dr.pptx

    【前端素材】大数据-兰州智慧消防大数据平台.zip

    大数据技术指的是用于处理和分析大规模数据集的技术和工具。以下是一些常见的大数据技术和工具: Hadoop:Apache Hadoop是一个用于分布式存储和处理大规模数据的开源框架。它包括Hadoop Distributed File System(HDFS)用于数据存储和MapReduce用于数据处理。 Spark:Apache Spark是一个快速、通用的集群计算系统,提供了比MapReduce更快的数据处理能力。它支持内存计算和更多复杂的数据处理流程。 NoSQL数据库:NoSQL数据库(如MongoDB、Cassandra等)则更适用于处理这类数据。 数据仓库:数据仓库是一个用于集成和分析大规模数据的存储系统,一些知名的数据仓库包括Snowflake、Amazon Redshift等。 数据湖:数据湖是一个存储结构化和非结构化数据的存储池,用于支持数据分析和机器学习应用。 机器学习:大数据技术也广泛应用于机器学习领域,支持大规模数据的模型训练和预测分析。 流式处理:针对实时数据处理需求,流式处理技术(如Apache Kafka、Apache Flink)可以实时。

    基于java的-152-springboot大学生体质测试管理系统--LW-源码.zip

    提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

    培训材料-云计算架构比较和关键技术讲解dr.ppt

    培训材料-云计算架构比较和关键技术讲解dr.ppt

    BP神经网络工具箱.doc

    数学模型算法

    基于python的-17-物业信息管理系统--LW-源码.zip

    提供的源码资源涵盖了python应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

    【前端素材】大数据-运营大数据.zip

    大数据技术指的是用于处理和分析大规模数据集的技术和工具。以下是一些常见的大数据技术和工具: Hadoop:Apache Hadoop是一个用于分布式存储和处理大规模数据的开源框架。它包括Hadoop Distributed File System(HDFS)用于数据存储和MapReduce用于数据处理。 Spark:Apache Spark是一个快速、通用的集群计算系统,提供了比MapReduce更快的数据处理能力。它支持内存计算和更多复杂的数据处理流程。 NoSQL数据库:NoSQL数据库(如MongoDB、Cassandra等)则更适用于处理这类数据。 数据仓库:数据仓库是一个用于集成和分析大规模数据的存储系统,一些知名的数据仓库包括Snowflake、Amazon Redshift等。 数据湖:数据湖是一个存储结构化和非结构化数据的存储池,用于支持数据分析和机器学习应用。 机器学习:大数据技术也广泛应用于机器学习领域,支持大规模数据的模型训练和预测分析。 流式处理:针对实时数据处理需求,流式处理技术(如Apache Kafka、Apache Flink)可以实时。

    node-v8.1.1.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v8.1.2.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于java的-160-springboot农机电招平台--LW-源码.zip

    提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

    小程序-58-童心党史小程序-源码.zip

    提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

    云计算基础课件—云安全dr.pptx

    云计算基础课件—云安全dr.pptx

    基于python开发的手写数字识别+源码(期末大作业&课程设计&项目开发)

    基于python开发的手写数字识别+源码,适合期末大作业、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于python开发的手写数字识别+源码,适合期末大作业、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于python开发的手写数字识别+源码,适合期末大作业、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于python开发的手写数字识别+源码,适合期末大作业、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~

    Zest是一款基于Spring的易于使用的单元测试工具(高分毕设).zip

    Java SSM项目是一种使用Java语言和SSM框架(Spring + Spring MVC + MyBatis)开发的Web应用程序。SSM是一种常用的Java开发框架组合,它结合了Spring框架、Spring MVC框架和MyBatis框架的优点,能够快速构建可靠、高效的企业级应用。 1. Spring框架:Spring是一个轻量级的Java开发框架,提供了丰富的功能和模块,用于开发企业级应用。它包括IoC(Inverse of Control,控制反转)容器、AOP(Aspect-Oriented Programming,面向切面编程)等特性,可以简化开发过程、提高代码的可维护性和可测试性。 2. Spring MVC框架:Spring MVC是基于Spring框架的Web框架,用于开发Web应用程序。它采用MVC(Model-View-Controller,模型-视图-控制器)的架构模式,将应用程序分为模型层、视图层和控制器层,提供了处理请求、渲染视图和管理流程的功能。 3. MyBatis框架:MyBatis是一个持久层框架,用于与数据库进行交互。它提供了一种将数据库操作与Java对象映射起来的方式,避免了手动编写繁琐的SQL语句,并提供了事务管理和缓存等功能,简化了数据库访问的过程

    数学建模模拟中的线性规划问题的数学模型.pptx

    数学模型算法

    基于python开发的口红色号识别程序+源码+开发文档+源码解析(毕业设计&课程设计&项目开发)

    基于python开发的口红色号识别程序+源码+开发文档+源码解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于python开发的口红色号识别程序+源码+开发文档+源码解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于python开发的口红色号识别程序+源码+开发文档+源码解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 项目简介: 口红色号识别器 选题: 基于图像处理、人脸识别和Flask构建的图片口红色号识别及商品推荐系统 功能: 根据上传的图片自动化图像处理后分析脸部特征,并判断其嘴部妆容状态,依托现有大数据分析,识别其口红色号,并推荐相关商品,通过Flask服务器框架返回到网页前端并动态呈现

    机械设计泵盖齿轮压装设备sw16可编辑非常好的设计图纸100%好用.zip

    机械设计泵盖齿轮压装设备sw16可编辑非常好的设计图纸100%好用.zip

Global site tag (gtag.js) - Google Analytics