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
If you sort the set of interval endpoints (both left and right endpoints together), and then process the interval endpoints from left to right (keeping track of what intervals they belong to), then in between each pair of consecutive endpoints you can record the number of intervals that overlap that subinterval in between the two endpoints (increasing the count by +1 every time you encounter a left-endpoint, and decreasing the count by -1 every time you encounter a right-endpoint). Then, given a query point, you just do binary search into the array of endpoints to find the two endpoints whose subinterval contains the query point, and report the number of intervals you previously computed that contain the subinterval. For N intervals and P query points total run time is O(N log N + P log N). Storage is O(N).
Isn't your find()
method returning one index further than the one you want?
You return the i
that caused the loop to exit. Therefore, either i == a.length
or a[i] > point
.
You should probably return i-1
instead. Moreover, this would be general enough for you not to have to deal with the special case a[0] > point
, which by the way breaks for an empty array.
private static int find(int[] a, int point) {
int i = 0;
while (i < a.length && a[i] <= point) i++;
return i-1;
}
If you really want to return one index further, then you shouldn't be returning -1
in your special case, but 0
instead. Which also removes the extra line of code:
private static int find(int[] a, int point) {
int i = 0;
while (i < a.length && a[i] <= point) i++;
return i;
}
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<T extends IntervalTree.Interval> {
// My intervals.
private final List<T> 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<T> left;
// My right tree. All intervals that start above my center.
private final IntervalTree<T> right;
public IntervalTree(List<T> 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<T> lefts = new ArrayList<T>();
// Rights contains all intervals that start above my center point.
final List<T> rights = new ArrayList<T>();
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<T>(lefts) : null;
right = rights.size() > 0 ? new IntervalTree<T>(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<T> 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<T> found = new ArrayList<T>();
// 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.<T>emptyList();
}
} else {
// To left.
return left != null ? left.query(point) : Collections.<T>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<Interval> intervals = new ArrayList<Interval>();
for (long[] pair : data) {
intervals.add(new SimpleInterval(pair[0], pair[1]));
}
// Build it.
IntervalTree<Interval> test = new IntervalTree<Interval>(intervals);
// Test it.
System.out.println("Normal test: ---");
for (long i = 0; i < 10; i++) {
List<Interval> 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<Interval>(intervals);
// Test it.
System.out.println("Empty test: ---");
for (long i = 0; i < 10; i++) {
List<Interval> intersects = test.query(i);
System.out.println("Point " + i + " intersects:");
for (Interval t : intersects) {
System.out.println(t.toString());
}
}
}
}