【问题标题】:Sweep Line Algorithm - Implementation for 1D plane扫描线算法 - 一维平面的实现
【发布时间】:2015-11-22 15:34:07
【问题描述】:

问题很简单,平面上有一些给定的一维线。 我们需要找到至少有一行的空间的总大小。

让我用一个示例图像来讨论这个问题-

这可能是一种情况。或者

可能是这种情况或类似情况。

我知道这是Sweep Line Algorithm的基本问题。

但是互联网上没有适当的文档可以正确理解。

我拥有的最好的一个是 Top Coder 的博客,那就是 here

但不清楚如何实现它或如何模拟。

如果我愿意,我们可以用 2 个循环在 O(n^2) 内完成,但我不知道过程会如何。

或者有没有比 O(n log n) 更好的算法?

任何人都可以通过分享任何 Sudo 代码或模拟来帮助我吗?

如果 Sudo 代码或示例代码不可用,那么从我可以实现的地方进行模拟就足够了。


Re- Problem calculating overlapping date ranges 不是我想要的,因为首先,它是 O(n^2),所以这不是我想要的。并且没有像这个问题那样完整描述。

【问题讨论】:

标签: algorithm sorting simulation implementation mark-and-sweep


【解决方案1】:

这是一种您可以尝试的方法(在 C# 中。我没有测试过,所以请原谅拼写错误等;只需采用“想法”/策略)。

性能为 O(N log m),其中 m 是您将创建的不相交“阴影”的数量。因此,最坏的情况(如果所有线段在阴影方面都不相交)你将有 O(N logN),但当你只有很少的阴影时,它基本上是 O(N)。

  public class LineSegment
  {
    public int Begin;
    public int End;
    // assumed to INCLUDE Begin but EXCLUDE End, so that
    // length = End-Begin

    public LineSegment Clone()
    {
      LineSegment clone = new LineSegment();
      clone.Begin=this.Begin;
      clone.End = this.End;
      return clone;
    }
  }

public int TotalShadow(LineSegment[] segments)
{
  // Class LineSegment has int members Begin and End, and Clone method to create a (shallow) Copy.
  // Can/should be adapted if we're dealing with LineSegments with double/float coordinates.

  // Easy special cases: no segements at all, or precisely one.
  int N = segments.Length;
  if (N == 0)
    return 0;
  else if (N == 1)
    return (segments[0].End - segments[0].Begin);

  // build a list of disjoint "shadows", cast onto the x-axis by all line segments together,
  // sorted by their "Begin" (leftmost coordinate).
  List<LineSegment> shadows = new List<LineSegment>();
  // Initialize with the first segment, for convenient iteration below.
  shadows.Add(segments[0].Clone());

  for (int k = 1; k < N; ++k) // start at #1: we handled #0 already.
  {
    // find its position (Begin) in relation to the existing shadows (binary search).
    int xBegin = segments[k].Begin;

    int jLow = 0;
    int xLow = shadows[jLow].Begin;

    int jHigh, xHigh;
    if (xBegin <= xLow)
      jHigh = jLow; // avoid any more binary searching below
    else
    {
      jHigh = shadows.Count - 1;
      xHigh = shadows[jHigh].Begin;
      if (xBegin >= xHigh)
        jLow = jHigh; // avoid any more binary searching below
    }

    while (jHigh - jLow > 1)
    {
      int jTry = (jLow + jHigh) / 2;
      int xTry = shadows[jTry].Begin;

      if (xTry <= xBegin)
        jLow = jTry;
      else
        jHigh = jTry;
    }

    // If it starts BEFORE "low" we create a new one: insert at jLow;
    // Elseif x falls INSIDE "low", we merge it with low;
    // ELSE we create a new shadow "between" low and high (as regards Begin)
    // In all cases we'll make sure jLow points to the applicable shadow (new or existing).
    // Next we'll check whether it overlaps with adjacent higher-lying shadows; if so: merge.
    if (xBegin < shadows[jLow].Begin)
      shadows.Insert(jLow, segments[k].Clone()); // jLow now points to the inserted item
    else if (xBegin <= shadows[jLow].End)
    { // merge: extend existing low if applicable.
      if (segments[k].End > shadows[jLow].End)
        shadows[jLow].End = segments[k].End;
    }
    else // if (xBegin > shadows[jLow].End)
      shadows.Insert(++jLow, segments[k].Clone()); // jLow increased to point to the inserted item.

   // Try to merge, starting at the next higher lying shadow.
    jHigh = jLow + 1;
    while (jHigh < N && shadows[jLow].End >= shadows[jHigh].Begin)
      jHigh++; // jHigh will point to the first one that we do NOT merge with.

    if (jHigh > jLow + 1) // any merges?
    {
      if (shadows[jHigh - 1].End > shadows[jLow].End)
        shadows[jLow].End = shadows[jHigh - 1].End; // set the appropriate End.

      for (int jRemove = jHigh - 1; jRemove > jLow; --jRemove)
        shadows.RemoveAt(jRemove); // Remove all shadaow-entries that we've merged with.
    }
  }

  // Wrap up.
  int shadowTotal = 0;
  foreach (LineSegment shadow in shadows)
    shadowTotal += (shadow.End - shadow.Begin);

  return shadowTotal;
}

【讨论】:

    【解决方案2】:

    也许你可以比盲排序稍微好一点,使用以下从 MergeSort 派生的方案:

    • 假设您有两个不重叠的区间列表,按递增界限排序;

    • 像在 MergeSort 中一样执行合并步骤(总是从每个列表移到最近的边界),以计算区间的并集;

    • 根据两个列表中索引的奇偶性,您可以判断要发出哪些边界(例如,将 AB 和 CDEF 与排序的 ACBDEF 合并,输出将为 ADEF)。

    这是一个线性时间操作。

    配备此修改后的合并,您可以实现修改后的 MergeSort,它以单个间隔开始并递归地形成联合。最后,您会得到一个间隔列表,这些间隔将为您提供所需的大小。

    在最坏的情况下,任何边界都不会消失,进程保持O(N Log(N))。但是当出现足够多的区间联合时,区间的数量会减少,处理时间会下降到线性O(N)

    【讨论】:

      【解决方案3】:

      这不是很复杂。

      形成一个数组,在其中放置所有区间端点横坐标,并带有一个标志,表明它是起点还是终点。

      对数组进行递增排序。

      然后在更新计数器的同时扫描数组:起点增加它,终点减少它。

      请求的大小很容易从计数器从零切换到非零的值中找到,反之亦然。

      我认为它不可能比 O(N Log(N)) 更快,因为排序(我认为这是无法避免的),除非数据允许线性时间排序(例如作为直方图排序)。

      【讨论】:

        【解决方案4】:

        创建一个结构数组/列表,其中包含段终点坐标X+1 属性作为起点,-1 属性作为终点A。 O(N)

        X 键对数组进行排序。 O(NlogN)

        CurrCount 初始化为零,遍历数组,将A 属性添加到CurrCount。 O(N)

        您将获得CurrCount 大于零(已覆盖)和CurrCount 为零(未覆盖)的范围

        【讨论】:

          【解决方案5】:

          没有太多关于这个主题的信息。

          所以,我正在与你分享我为你创建的算法和模拟,它也是 O(n log n) !!!!!!

          让我们开始吧-

          1. 创建所有行动点的优先级列表(行动点是一条线的起点或终点)。 PQ 的每一项都有 3 个元素(当前点、起点或终点、来自哪条线)。 (O(n log n) 操作,如果我们使用Quick Short 进行排序)。
          2. 初始化一个用于存储当前活动行的向量。
          3. 初始化一个大小 = 行数 + 1 的数组(用于存储阴影长度的总和)。

          1. 现在从 PQ 中删除一个项目,并为该项目运行特定操作,如下图所述,您就完成了。

          0

          1

          2

          3

          4

          5

          6

          7

          8

          9

          10

          11

          1. 一直这样做,直到 PQ 为空。

          在您的情况下,找到数组从 1 到结尾(索引号 1 到 m)的所有元素的总和,这就是您的答案。

          但是使用这种算法和数组,您可以轻松获得许多更复杂的问题答案,例如具有 3 个阴影 = Arr3 的空间长度是多少等等。

          现在的问题是订单是什么,对吗?

          所以,排序 = O(n log n) 和扫荡 = O(m) [m=没有动作点,所以 m

          所以,总顺序是 = O(n log n) + O(m) = O(n log n)

          认为您可以轻松理解它,并且对您和其他许多人都有很大的帮助。并且认为您将能够轻松实现它。

          【讨论】:

          • 这是什么,请先在google上搜索一下再问。我自己做了所有的。
          • 我为其他人做过这件事,因为我认为他们不应该面临我所面临的同样问题。如果你喜欢,请为它投票。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-06-23
          • 2020-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多