合并天际线,分而治之

Posted

技术标签:

【中文标题】合并天际线,分而治之【英文标题】:Merge skylines, divide and conquer 【发布时间】:2013-02-05 07:20:37 【问题描述】:

我正在尝试解决著名的天际线问题(见 gif):

输入

(1,11,5), (2,6,7), (3,13,9), (12,7,16), (14,3,25), (19,18,22) , (23,13,29), (24,4,28)

应该返回,其他建筑物后面的点应该没有了,Y轴变化的坐标应该在返回的天际线中:

(1, 11), (3, 13), (9, 0), (12, 7), (16, 3), (19, 18), (22, 3), (23, 13) , (29, 0)

我试图通过对算法使用分而治之的方法来实现 O(n lg n) 的运行时间,但我被困在合并部分。

每当我想到它时,我都会感到困惑。例如,我检查天际线首先是哪一个,例如它具有较低的 x 坐标,然后我将 + 它的高度添加到我的新天际线中。但是后来我遇到了在另外两个天际线后面的天际线,我怎样才能检测到这种“碰撞”?

我是否首先通过检查 left.x2

也许我想得太复杂了?我需要的是一组最高的 y 坐标,在每个路口,我应该这样接近它吗?

【问题讨论】:

【参考方案1】:

我认为这应该是一种更容易理解的方法:

将 x 坐标拆分为每个矩形的开始和结束对象,如下所示:

rect(x1, y, x2) -> (x1, y, "start", reference to rect),
                   (x2, y, "finish", reference to rect)

比如:

class MyStruct

   Rectangle rect;
   int x, y;
   bool isStart;

按 x 坐标对这些对象进行排序 (O(n log n)) 创建一组(最初为空)矩形(将按 y 坐标排序,例如 BST) 循环遍历这些对象(按现在排序的顺序)(O(n)) 每当遇到启动时 将矩形添加到矩形集中 (O(log n)) 如果是新的最高矩形,则将该起点添加到输出 (O(1)) 每当遇到完成时 从矩形集中移除矩形 (O(log n)) 如果是最高矩形,则在集合中找到下一个最高矩形并将点(current.finishX, new.y) 添加到输出(O(1))(如果集合为空,则将(current.finishX, 0) 添加到输出)李>

所以O(n log n)

【讨论】:

让我看看如果我理解正确,通过拆分您的意思的 x 坐标,例如,(2, 4, 8) -> (2, 4), (8, 0), 对? 抱歉,我不清楚您所说的“当前矩形”是什么意思。只是初始矩形的列表? 对不起,也许我太笨了,但我还是不明白。每当遇到 start 时:将其添加到矩形集合中,但我们刚刚确定这些结构不是矩形 @Oxymoron 您将对象的矩形成员添加到集合中。 如何“找到集合中的下一个最高矩形”O(1)?【参考方案2】:

这可以通过修改归并排序算法来实现。天际线的算法如下:

构建天际线

    ConstructSkyLine(List B ) --------------- O(nlogn)
    
        If(B.size() == 1)
        
            List skyLineList = new List();
            SKYLINE = new SKYLINE(B.XL, B.XR, B.H);
            skyLineList.add(SKYLINE);
            Return skyLineList;
        
        B1, B2 <-- Split(B);
        List S1 <-- ConstructSkyLine(B1);
        List S2 <-- ConstructSkyLine(B2);
        List S <-- MergeSkyline(S1, S2);
        Return S;
    

合并天际线

    MergeSkyline(List S1, List S2) --------------- O(n)
    
        List< SKYLINEENTRY> skylineEntryList = new ArrayList<SKYLINEENTRY>();
        while (S1.isEmpty() && S2.isEmpty())--------------- O(n)
        
           S1Item <-- S1.get(0);
           S2Item <-- S2.get(0);
           If (S1Item.XL < S2Item.XL)
           
             Merge(S1, S2, skylineEntryList);   --------------- O(n)
           
           Else
           
             Merge(S2, S1, skylineEntryList); --------------- O(n)
           
         

         If(!S1.isEmpty())
         
            skylineEntryList.add(S1);
         

         If(!S2.isEmpty())
         
           skylineEntryList.add(S2);
         
         Retrun skylineEntryList;
      

合并

  Merge(S1, S2, skylineEntryList) --------------- O(n)
  
    SKYLINEENTRY <-- null;
    S1Item <-- S1.get(0);
    S2Item <-- S2.get(0);
    SKYLINEENTRY.XL = S1Item.XL;
    If(S1Item.XR > S2Item.XL) // means over lap 
    
        If(S1Item.H > S2Item.H) // S1 is higher.
        
           SKYLINEENTRY.XR = S1Item.XR;
           SKYLINEENTRY.H = S1Item.H;
           S1.remove(S1Item); --------------- O(n)
           skylineEntryList.add(SKYLINEENTRY);
           if(S2Item.XR < S1Item.XR) // full overlap
           
              S2.remove(S2Item); --------------- O(n)
           
           Else // partial overlap
           
              S2Item.XL <-- S1Item.XR;
                       
        
        Else //S2 is higher
        
           SKYLINEENTRY.XR = S2Item.XL;
           SKYLINEENTRY.H = S1Item.H;
           if(S2Item.XR < S1Item.XR) // full overlap with S2
           
              S1Item.XL = S2Item.XR;
           
           Else // partial overlap
           
              S1.remove(S1Item); --------------- O(n)
               
           skylineEntryList.add(SKYLINEENTRY);  
           
     
     Else // no overlap
     
        SKYLINEENTRY.XR = S1Item.XR;
        SKYLINEENTRY.H = S1Item.H;
        S1.remove(S1Item); --------------- O(n)
        skylineEntryList.add(SKYLINEENTRY);
     
  

【讨论】:

以上是关于合并天际线,分而治之的主要内容,如果未能解决你的问题,请参考以下文章

算法设计一:分而治之

分治策略合并多个排序数组

分而治之的概念

C语言中的分而治之二分查找

分而治之——为啥有效?

归并排序和快速排序