在矩形流中快速搜索

时间:2015-03-31 00:15:36

标签: java algorithm search

问题在于:

我有一个轴对齐的矩形流,由它们的对角端点[x1,y1]和[x2,y2]描述,其中x1

在某些时候,我会得到一个观点:[x,y]。我需要找到包含这一点的所有矩形。

最快的方法是什么?我想以插入为代价优化查找时间。由于它是一个流,我不必在查找期间从头开始构建这个结构。

我不确定范围树是否适合这个,因为[x1,y1]和[x2,y2]不是独立的。

编辑:请注意,在我得到查询后,我将从流中获得更多矩形,然后是另一个查询点,依此类推。此外,我可能还会被要求忽略矩形,因此也应该支持删除。 如果我可以使用现有的Java库来实现它,而不必实现我自己的间隔/段树,那么就可以获得奖励。

2 个答案:

答案 0 :(得分:1)

我认为你可以通过分别考虑两个维度来简化这个问题。

构建一个有序数据结构,该结构具有到目前为止范围的每个部分的条目,该条目共享同一组封闭矩形X维度范围。到目前为止,分割点将是输入中的x1或x2的值。对于每个子范围,存储X维范围涵盖该子范围的矩形集。

对Y维做同样的事情。

要查找[x,y],请使用二分搜索或类似方法查找包含x的X维度子范围和包含y的Y维度子范围。这两个子范围的矩形集的交集是一组矩形,每个矩形包含[x,y]。

这是Java中基本的概念验证实现,更像是伪代码来澄清这个想法而不是完整的实现。我做了非常有限的测试。我使用ArrayList来跟踪范围 - 真正的实现更可能使用某种形式的平衡树。

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class RectangleTest {
  RangeList xRanges = new RangeList();
  RangeList yRanges = new RangeList();


  public static void main(String[] args) {
    RectangleTest rectangleTest = new RectangleTest();
    rectangleTest.test();
  }

  private void test() {
    Rectangle[] rectangles = new Rectangle[]{
        new Rectangle(2,100,4,102),
        new Rectangle(2,102,4,200),
    };
    add(rectangles[0]);
    add(rectangles[1]);
    testit(0,0);
    testit(3,101);
    testit(4,101);
    testit(4,102);
    delete(rectangles[0]);
    testit(3,101);
    testit(4,101);
    testit(4,102);
  }

  private void testit(int x, int y){
    System.out.println("("+x+","+y+") is in "+getRectangles(x,y));
  }

  void add(Rectangle rectangle) {
    System.out.println("Adding: "+rectangle);
    xRanges.addRectangle(rectangle.x1, rectangle.x2, rectangle);
    yRanges.addRectangle(rectangle.y1, rectangle.y2, rectangle);
  }

  void delete(Rectangle rectangle){
    System.out.println("Deleting: "+rectangle);
    xRanges.deleteRectangle(rectangle.x1, rectangle.x2, rectangle);
    yRanges.deleteRectangle(rectangle.y1, rectangle.y2, rectangle);
  }

  Set<Rectangle> getRectangles(int x, int y){
    Set<Rectangle> result = xRanges.lookup(x);
    result.retainAll(yRanges.lookup(y));
    return result;
  }
}

/* A Range represents a range of locations in one dimension, and
 * and associated set of rectangles.
 */
class Range {
  int start;
  Set<Rectangle> rectangles;

  Range(int start) {
    this(start, new HashSet<Rectangle>());
  }

  Range(int start, Set<Rectangle> rectangles) {
    this.start = start;
    this.rectangles = rectangles;
  }

  void add(Rectangle rectangle) {
    rectangles.add(rectangle);
  }

  void remove(Rectangle rectangle) {
    rectangles.remove(rectangle);
  }
}

/* A RangeList represents all ranges in one dimension.*/
class RangeList {
  List<Range> ranges = new ArrayList<Range>();
  static final int endAll = Integer.MAX_VALUE;

  RangeList() {
    ranges.add(new Range(0));
  }

  void addRectangle(int start, int end, Rectangle rectangle) {
    int startIndex = getIndex(start);
    Range startRange = ranges.get(startIndex);
    if (start > startRange.start) {
      // Need to split the start range
      startIndex++;
      ranges.add(startIndex, new Range(start, new HashSet<Rectangle>(
          startRange.rectangles)));
    }
    int endIndex = getIndex(end);
    Range endRange = ranges.get(endIndex);
    if (end > endRange.start) {
      // Need to split the end range
      ranges.add(endIndex + 1, new Range(end, new HashSet<Rectangle>(
          endRange.rectangles)));
    }
    // Add the rectangle to its ranges
    for (int i = startIndex; i <= endIndex; i++) {
      ranges.get(i).rectangles.add(rectangle);
    }

  }

  void deleteRectangle(int start, int end, Rectangle rectangle) {
    int startIndex = getIndex(start);
    int endIndex = getIndex(end);
    // Remove the rectangle from each range it is in
    for (int i = startIndex; i <= endIndex; i++) {
      ranges.get(i).rectangles.remove(rectangle);
    }
    // Merge ranges that now have equal rectangle sets
    for (int i = endIndex; i >= Math.max(startIndex, 1); i--) {
      if (ranges.get(i).rectangles.equals(ranges.get(i - 1).rectangles)) {
        ranges.remove(i);
      }
    }
  }

  Set<Rectangle> lookup(int location) {
    int index = getIndex(location);
    Range range = ranges.get(index);
    Set<Rectangle> result = new HashSet<Rectangle>(range.rectangles);
    if (location == range.start && index > 0) {
      // On the boundary, include ranges ending at location
      result.addAll(ranges.get(index - 1).rectangles);
    }
    return result;
  }

  /* Find the index of the range containing the location. For this
   * purpose only, ranges are treated as being closed on the left, open
   * on the right, so that every point is in exactly one range.
   */
  int getIndex(int location) {
    int rangeCount = ranges.size();
    if (rangeCount == 1) {
      return 0;
    }
    return getIndex(location, 0, rangeCount - 1);
  }

  /* Get the index of the range containing the location, as above, but
   * assuming the index is in [start,end]. Recursive binary search.
   */
  private int getIndex(int location, int start, int end) {
    if (start == end) {
      return start;
    }
    int mid = (start + end + 1) >>> 1;
    if (location < ranges.get(mid).start) {
      return getIndex(location, start, mid - 1);
    } else {
      return getIndex(location, mid, end);
    }
  }
}

class Rectangle {
  int x1;
  int y1;
  int x2;
  int y2;

  public Rectangle(int x1, int y1, int x2, int y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x1;
    result = prime * result + x2;
    result = prime * result + y1;
    result = prime * result + y2;
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    Rectangle other = (Rectangle) obj;
    if (x1 != other.x1)
      return false;
    if (x2 != other.x2)
      return false;
    if (y1 != other.y1)
      return false;
    if (y2 != other.y2)
      return false;
    return true;
  }

  public String toString() {
    return "[" + x1 + "," + y1 + "][" + x2 + "," + y2 + "]";
  }
}

答案 1 :(得分:0)

R-tree旨在有效管理此类查询