扫描线算法 - 1D平面的实现

时间:2015-08-28 04:03:04

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

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

让我用示例图像讨论这个 -

Seniario 1

这可能是个案。或

Seniario 2

这可能是个案或类似的事情。

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

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

我所拥有的最好的是 Top Coder 的博客,即here

但目前尚不清楚如何实施它或如何进行模拟。

如果我愿意,我们可以在O(n ^ 2)中使用2个循环进行,但我无法意识到该程序将如何。

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

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

如果Sudo代码或示例代码不可用,那么我可以通过模拟来实现这一点。

重新 Problem calculating overlapping date ranges不是我想要的,因为首先,它是O(n ^ 2)因此,它不是我想要的。并没有像这个问题那样完全描述。

5 个答案:

答案 0 :(得分:6)

此主题的信息不多。

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

让我们开始 -

  1. 创建所有操作点的优先级列表(操作点是一行的起点或终点)。 PQ的每个项目都有3个元素(当前点,开始或结束,来自什么线)。 (如果我们使用Quick Short进行排序,则 O(n log n)操作。
  2. 初始化Vector以存储当前活动行。
  3. 初始化一个size = no of lines + 1的数组(用于存储阴影长度的总和)。
  4. Data Structure

    1. 现在从 PQ 中删除项目,然后对该项目执行特定操作,如下图所示,您就完成了。
    2. Sim 0

      0

      Sim 1

      1

      2

      2

      3

      3

      4

      4

      5

      5

      6

      6

      7

      7

      8

      8

      9

      9

      10

      10

      End

      11

      1. 并且直到PQ为空。
      2. 在你的情况下,找到数组中从1到结尾的所有元素的总和(索引号1到m),这是你的答案。

        但是使用这个算法和数组,您可以轻松地获得更复杂的问题答案,例如3 shadow = Arr 3的空间长度等等。

        现在问题是关于订单的内容,对吧?

        所以,排序= O(n log n) 并且扫描= O(m)[m =没有动作点,所以m

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

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

答案 1 :(得分:0)

创建一个数组/结构列表,其中包含起点的段结束点坐标X+1属性以及结束点-1的{​​{1}}属性。 O(N)

A键排序数组。 O(NlogN)

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

您将获得CurrCount大于零(覆盖)的范围,以及CurrCount为零(未覆盖)的范围

答案 2 :(得分:0)

这是你可以尝试的方法(在C#中。我没有测试过,所以请原谅错字等等;只需采取“想法”/策略)。

性能为O(N log m),其中m是您将创建的不相交“阴影”的数量。所以,最糟糕的情况(如果所有的linesegments与它们的阴影不相交)你将有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;
}

答案 3 :(得分:0)

这不是很复杂。

形成一个数组,其中您将所有区间端点abscissas都带有一个标志,告知它是起点还是终点。

越来越多地排序数组。

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

从计数器从零切换到非零的值可以很容易地找到所需的大小。反之。

我不认为有可能使它比O(N Log(N))快,因为排序(我认为不能避免),除非数据允许线性时间排序(如直方图排序)。

答案 4 :(得分:0)

使用从MergeSort派生的以下方案,使用盲目排序可能会稍微好一些:

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

  • 执行合并步骤,如MergeSort(始终移动到每个列表中最近的边界),以计算间隔的并集;

  • 根据两个列表中索引的奇偶校验,可以判断要发出哪些边界(f.i.合并AB和CDEF与排序ACBDEF,输出将是ADEF)。

这是线性时间操作。

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

在最坏的情况下,没有约束会消失,过程仍为O(N Log(N))。但是当出现足够多的区间联合时,间隔的数量会减少,处理时间也会缩短到线性O(N)