简体   繁体   English

给定一组间隔,找出包含一个点的间隔

[英]Given a set of intervals, find how many intervals contain a point

Suppose you are given a set of N intervals (represented as left and right coordinates) and M points. 假设您得到了一组N个间隔(分别表示为左右坐标)和M个点。 For each point P algorithm should find number of intervals to which P belongs. 对于每个点,P算法应找到P所属的区间数。

Here's my algorithm: 这是我的算法:

1) Put left and right coordinates of intervals in "left" and "right" arrays respectively 1)将间隔的左坐标和右坐标分别放在“左”和“右”数组中

2) Sort "left", exchanging entries simultaneously with "right" 2)排序“左”,同时与“右”交换条目

3) Given a point P, find a maximum i such that left[i] <= P 3)给定一个点P,找到一个最大值i,使得left [i] <= P

4) For each j <= i, add 1 to result if right[j] >= P 4)对于每个j <= i,如果right [j]> = P,则将结果加1

5) Return result 5)返回结果

Implementation in Java: 用Java实现:

import java.util.*;

class Intervals {

    public  static int count(int[] left, int[] right, int point) {
        int k = find(left, point), result = 0;
        for (int i=0; i < k; i++)
            if (point <= right[i]) result++;
        return result;
    }


    private static int find(int[] a, int point) {
        if (point < a[0]) return -1;
        int i = 0;
        while (i < a.length && a[i] <= point) i++;
        return i;
    }

    private static void sort(int[] a, int[] b) {
        sort(a, b, 0, a.length-1);
    }

    private static void sort(int[] left, int[] right, int lo, int hi) {
        if (hi <= lo) return;
        int lt = lo, gt = hi;
        exchange(left, right, lo, lo + (int) (Math.random() * (hi-lo+1)));
        int v = left[lo];
        int i = lo;
        while (i <= gt) {
            if      (left[i] < v) exchange(left, right, lt++, i++);
            else if (left[i] > v) exchange(left, right, i, gt--);
            else                  i++;
        }
        sort(left, right, lo, lt-1);
        sort(left, right, gt+1, hi);
    }

    private static void exchange(int[] left, int[] right, int i, int j) {
        int temp  = left[i];
        left[i]   = left[j];
        left[j]   = temp;
        temp      = right[i];
        right[i]  = right[j];
        right[j]  = temp;
    }

    private static boolean less(int[] a, int i, int j) {
        return a[i] < a[j];
    }


    public static void main(String[] args) {
        int n       = Integer.parseInt(args[0]);
        int m       = Integer.parseInt(args[1]);
        int[] left  = new int[n];
        int[] right = new int[n];
        Random r    = new Random();
        int MAX     = 100000;
        for (int i = 0; i < n; i++) {
            left[i] = r.nextInt(MAX);
            right[i] = left[i] + r.nextInt(MAX/4);
        }
        sort(left, right);
        for (int i=0; i < m; i++)
            System.out.println(count(left, right, r.nextInt(MAX)));
    }
}

This code has not passed some test, and I'm trying to find a bug. 这段代码尚未通过测试,我正在尝试查找错误。 Point is that I actually don't know what input data used in these tests. 关键是我实际上不知道这些测试中使用了什么输入数据。

Thanks. 谢谢。

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());
      }
    }

  }
}

Isn't your find() method returning one index further than the one you want? 您的find()方法不是比您想要的索引返回一个索引吗?

You return the i that caused the loop to exit. 您返回导致循环退出的i Therefore, either i == a.length or a[i] > point . 因此, i == a.lengtha[i] > point

You should probably return i-1 instead. 您可能应该改为返回i-1 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. 而且,对于您而言,不必处理特殊情况a[0] > point ,这通常就足够了,顺便说一句,它会破坏一个空数组。

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. 如果您确实想进一步返回一个索引,则在特殊情况下不应返回-1 ,而应返回0 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;
}

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). 如果您对间隔端点集(左右端点同时排序)进行排序,然后从左到右处理间隔端点(跟踪它们属于什么间隔),则可以在每对连续端点之间记录与两个端点之间的子间隔重叠的间隔数(每次遇到左端点时,计数增加+1,遇到右端点时,计数减少-1)。 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). 对于N个间隔和P个查询点,总运行时间为O(N log N + P log N)。 Storage is O(N). 存储为O(N)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM