查找所有矩形区域的算法

时间:2017-11-12 20:17:33

标签: algorithm

让我们说我们得到4点形式的矩形:它们(x1,y1),...,(x4,y4) 我们想要计算它们所涵盖的总面积。我们想要计算总面积,如果更多的矩形重叠,我们只计算一次该区域。

我不是真的在寻找完整的解决方案,伪代码或一些有用的算法和数据结构的链接,我们将不胜感激。

矩形的形式是:由三个整数给出:左侧位置,右侧位置和高度。例如:

L:0 R:2 H:2

L:1 R:3 H:3

L:-1 R:4 H:1

总面积为:10

x轴的最大值是-1e9到1e9,从x = L开始,到x = R结束 y不能低于0并始终从y = 0开始并在y = H

处结束

2 个答案:

答案 0 :(得分:3)

让我们假设你的矩形在很小的范围内有整数坐标,比如0到10.然后一个简单的方法是创建一个网格并将矩形绘制到它上面:

Unit coordinate grid

占用的“像素”可以存储在一个集合中,或者它们可以在位图中设置位。当矩形重叠时,交叉点再次被标记为占用,因此仅对该区域贡献一次。该区域是占用单元的数量。

对于大尺寸,数据结构会变得太大。另外,绘制一个宽度为几百万像素的矩形会很慢。

但是当我们使用压缩坐标时,仍然可以应用该技术。那么你的网格只有坐标是矩形的实际坐标。细胞具有可变的宽度和高度。单元格的数量取决于矩形的数量,但它与最小和最大坐标无关:

Compressed coordinate grid

算法如下所示:

  • 找到所有唯一的 x 坐标。按升序对它们进行排序,并在字典中存储(坐标,索引)对,以便于查找。
  • y 坐标执行相同的操作。
  • 绘制矩形:找到 x y 范围的索引并占据它们之间的单元格。
  • 通过对所有占用单元格的面积求和来计算面积。

答案 1 :(得分:1)

这些矩形的基数为y = 0?我认为这是真的。所以这些就像从远处看城市中的建筑物。你正试图追踪天际线。

将矩形存储在一个数组中,以便您可以将数组索引用作唯一ID。将每个左右矩形边缘表示为"事件"包括它所属矩形的ID和相应边的x坐标。将所有事件放在EL列表中并按x坐标排序。最后,您需要一个按相应矩形高度降序排序的矩形ID的动态排序集(例如Java TreeSet)。它被称为SL,"扫描线"。由于它的排序方式,SL.first始终是SL当前引用的最高矩形的ID。

现在您可以按如下方式绘制矩形集合的轮廓:

SL = <empty>  // sweep line
x0 = EL.first.left // leftmost x among all rectangle edges
lastX = x0
for each event E in EL // process events left-to-right
  Let y0 = if SL.isEmpty then 0 else SL.first.height // current y
  if E.ID in SL // event in SL means sweep line is at rectangle's right edge
    remove E.ID from SL
  else // event means sweep line is a new rectangle's left edge
    add E.ID to SL
  Let y1 = if SL.isEmpty then 0 else SL.first.height // new y
  if y1 != y0
    output line seg (lastX, y0) -> (E.x, y0)
    output line seg (E.x, y0) -> (E.x, y1)
    lastX = E.x
output final line seg (lastX, 0) -> (x0, 0)

因为这听起来像是家庭作业或者是面试问题,我会让你修改这个算法来提供扫掠形状的区域,而不是画出它的边缘。

<强>加成

只是为了好玩:

import java.util.ArrayList;
import static java.lang.Integer.compare;
import static java.util.Arrays.stream;
import static java.util.Collections.sort;
import java.util.Comparator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

class SkyLine {
  static class Rectangle {
    final int left;
    final int right;
    final int height;
    Rectangle(int left, int right, int height) {
      this.left = left;
      this.right = right;
      this.height = height;
    }
  }
  static class Event implements Comparable<Event> {
    final int x;
    final int id;
    public Event(int x, int id) {
      this.x = x;
      this.id = id;
    }
    @Override
    public int compareTo(Event e) { return compare(x, e.x); }
  }

  final List<Rectangle> rectangles = new ArrayList<>();  
  final Comparator byHeightDescending = 
      (Comparator<Integer>) (Integer a, Integer b) -> 
          compare(rectangles.get(b).height, rectangles.get(a).height);
  final SortedSet<Integer> scanLine = new TreeSet<>(byHeightDescending);
  final List<Event> events = new ArrayList<>();

  SkyLine(Rectangle [] data) {
    stream(data).forEach(rectangles::add);
    int id = 0;
    for (Rectangle r : rectangles) {
      events.add(new Event(r.left, id));
      events.add(new Event(r.right, id));
      ++id;
    }
    sort(events);
  }

  int area() {
    int area = 0;
    Event ePrev = null;
    for (Event e : events) {
      if (ePrev != null) area += (e.x - ePrev.x) * rectangles.get(scanLine.first()).height;
      if (!scanLine.remove(e.id)) scanLine.add(e.id);
      ePrev = e;
    }
    return area;
  }

  public static void main(String [] args) {
    Rectangle [] data = {
      new Rectangle(0, 2, 2),
      new Rectangle(1, 3, 3),
      new Rectangle(-1, 4, 1),
    };
    int area = new SkyLine(data).area();
    System.out.println(area);
  }
}