Suppose you are given a set of N intervals (represented as left and right coordinates) and M points. For each point P algorithm should find number of intervals to which P be
Probably not the answer you are looking for but may be the answer to someone coming across this question another day.
If you are planning to query a fairly static set of ranges often then you may wish to consider an Interval Tree.
public class IntervalTree {
// My intervals.
private final List intervals;
// My center value. All my intervals contain this center.
private final long center;
// My interval range.
private final long lBound;
private final long uBound;
// My left tree. All intervals that end below my center.
private final IntervalTree left;
// My right tree. All intervals that start above my center.
private final IntervalTree right;
public IntervalTree(List intervals) {
if (intervals == null) {
throw new NullPointerException();
}
// Initially, my root contains all intervals.
this.intervals = intervals;
// Find my center.
center = findCenter();
/*
* Builds lefts out of all intervals that end below my center.
* Builds rights out of all intervals that start above my center.
* What remains contains all the intervals that contain my center.
*/
// Lefts contains all intervals that end below my center point.
final List lefts = new ArrayList();
// Rights contains all intervals that start above my center point.
final List rights = new ArrayList();
long uB = Long.MIN_VALUE;
long lB = Long.MAX_VALUE;
for (T i : intervals) {
long start = i.getStart();
long end = i.getEnd();
if (end < center) {
lefts.add(i);
} else if (start > center) {
rights.add(i);
} else {
// One of mine.
lB = Math.min(lB, start);
uB = Math.max(uB, end);
}
}
// Remove all those not mine.
intervals.removeAll(lefts);
intervals.removeAll(rights);
uBound = uB;
lBound = lB;
// Build the subtrees.
left = lefts.size() > 0 ? new IntervalTree(lefts) : null;
right = rights.size() > 0 ? new IntervalTree(rights) : null;
// Build my ascending and descending arrays.
/**
* @todo Build my ascending and descending arrays.
*/
}
/*
* Returns a list of all intervals containing the point.
*/
List query(long point) {
// Check my range.
if (point >= lBound) {
if (point <= uBound) {
// In my range but remember, there may also be contributors from left or right.
List found = new ArrayList();
// Gather all intersecting ones.
// Could be made faster (perhaps) by holding two sorted lists by start and end.
for (T i : intervals) {
if (i.getStart() <= point && point <= i.getEnd()) {
found.add(i);
}
}
// Gather others.
if (point < center && left != null) {
found.addAll(left.query(point));
}
if (point > center && right != null) {
found.addAll(right.query(point));
}
return found;
} else {
// To right.
return right != null ? right.query(point) : Collections.emptyList();
}
} else {
// To left.
return left != null ? left.query(point) : Collections.emptyList();
}
}
private long findCenter() {
//return average();
return median();
}
/**
* @deprecated Causes obscure issues.
* @return long
*/
@Deprecated
protected long average() {
// Can leave strange (empty) nodes because the average could be in a gap but much quicker.
// Don't use.
long value = 0;
for (T i : intervals) {
value += i.getStart();
value += i.getEnd();
}
return intervals.size() > 0 ? value / (intervals.size() * 2) : 0;
}
protected long median() {
// Choose the median of all centers. Could choose just ends etc or anything.
long[] points = new long[intervals.size()];
int x = 0;
for (T i : intervals) {
// Take the mid point.
points[x++] = (i.getStart() + i.getEnd()) / 2;
}
Arrays.sort(points);
return points[points.length / 2];
}
void dump() {
dump(0);
}
private void dump(int level) {
LogFile log = LogFile.getLog();
if (left != null) {
left.dump(level + 1);
}
String indent = "|" + StringUtils.spaces(level);
log.finer(indent + "Bounds:- {" + lBound + "," + uBound + "}");
for (int i = 0; i < intervals.size(); i++) {
log.finer(indent + "- " + intervals.get(i));
}
if (right != null) {
right.dump(level + 1);
}
}
/*
* What an interval looks like.
*/
public interface Interval {
public long getStart();
public long getEnd();
}
/*
* A simple implemementation of an interval.
*/
public static class SimpleInterval implements Interval {
private final long start;
private final long end;
public SimpleInterval(long start, long end) {
this.start = start;
this.end = end;
}
public long getStart() {
return start;
}
public long getEnd() {
return end;
}
@Override
public String toString() {
return "{" + start + "," + end + "}";
}
}
/**
* Not called by App, so you will have to call this directly.
*
* @param args
*/
public static void main(String[] args) {
/**
* @todo Needs MUCH more rigorous testing.
*/
// Test data.
long[][] data = {
{1, 2},
{2, 9},
{4, 8},
{3, 5},
{7, 9},};
List intervals = new ArrayList();
for (long[] pair : data) {
intervals.add(new SimpleInterval(pair[0], pair[1]));
}
// Build it.
IntervalTree test = new IntervalTree(intervals);
// Test it.
System.out.println("Normal test: ---");
for (long i = 0; i < 10; i++) {
List intersects = test.query(i);
System.out.println("Point " + i + " intersects:");
for (Interval t : intersects) {
System.out.println(t.toString());
}
}
// Check for empty list.
intervals.clear();
test = new IntervalTree(intervals);
// Test it.
System.out.println("Empty test: ---");
for (long i = 0; i < 10; i++) {
List intersects = test.query(i);
System.out.println("Point " + i + " intersects:");
for (Interval t : intersects) {
System.out.println(t.toString());
}
}
}
}