查找许多线段的联合长度

时间:2013-02-10 11:43:47

标签: algorithm

我在x轴上以其开始和结束x坐标的形式有一些粗体线段。某些线段可能重叠。如何找到所有线段的并集长度。

示例,线段为5,0到8,0,其他为9,0到12,0。两者都不重叠,因此长度之和为3 + 3 = 6.

线段为5,0到8,0,其他线段为7,0到12,0。但它们在范围上重叠,即7,0到8,0。所以长度的联合是7。

但x坐标可能是浮点。

4 个答案:

答案 0 :(得分:1)

将线段表示为2 EndPoint个对象。每个EndPoint对象的格式为<coordinate, isStartEndPoint>。将所有线段的所有EndPoint个对象放在列表endPointList中。

算法:

  • 按坐标按升序排序endPointList第一然后将起点终点放在尾端点的前面(无论哪个段,因为它无关紧要 - 所有都在同一坐标上。
  • 根据此伪代码循环遍历排序列表:

    prevCoordinate = -Inf
    numSegment = 0
    unionLength = 0
    
    for (endPoint in endPointList):
        if (numSegment > 0):
            unionLength += endPoint.coordinate - prevCoordinate
    
        prevCoordinate = endPoint.coordinate
    
        if (endPoint.isStartCoordinate):
            numSegment = numSegment + 1
        else:
            numSegment = numSegment - 1
    

numSegment变量将告诉我们是否在某个细分受众群中。当它大于0时,我们在某个段内,所以我们可以包括到前一个终点的距离。如果为0,则表示当前结束点之前的部分不包含任何段。

复杂性由排序部分主导,因为基于比较的排序算法具有较低的Omega(n log n)界限,而循环显然是O(n)。因此,如果选择基于O(n log n)比较的排序算法,算法的复杂性可以说是O(n log n)。

答案 1 :(得分:0)

使用范围树。范围树是n log(n),就像排序的开始/结束点一样,但它还有一个额外的好处,即重叠范围会减少元素的数量(但可能会增加插入成本)Snippet(未经测试)

struct segment {
        struct segment *ll, *rr;
        float lo, hi;
        };

struct segment * newsegment(float lo, float hi) {
struct segment * ret;
ret = malloc (sizeof *ret);
ret->lo = lo; ret->hi = hi;
ret->ll= ret->rr = NULL;
return ret;
}

struct segment * insert_range(struct segment *root, float lo, float hi)
{
if (!root) return newsegment(lo, hi);

        /* non-overlapping(or touching)  ranges can be put into the {l,r} subtrees} */
if (hi < root->lo) {
        root->ll = insert_range(root->ll, lo, hi);
        return root;
        }
if (lo > root->hi) {
        root->rr = insert_range(root->rr, lo, hi);
        return root;
        }

        /* when we get here, we must have overlap; we can extend the current node
        ** we also need to check if the broader range overlaps the child nodes
        */
if (lo < root->lo ) {
        root->lo = lo;
        while (root->ll && root->ll->hi >= root->lo) {
                struct segment *tmp;
                tmp = root->ll;
                root->lo = tmp->lo;
                root->ll = tmp->ll;
                tmp->ll = NULL;
                // freetree(tmp);
                }
        }
if (hi > root->hi ) {
        root->hi = hi;
        while (root->rr && root->rr->lo <= root->hi) {
                struct segment *tmp;
                tmp = root->rr;
                root->hi = tmp->hi;
                root->rr = tmp->rr;
                tmp->rr = NULL;
                // freetree(tmp);
                }
        }

return root;
}

float total_width(struct segment *ptr)
{
float ret;
if (!ptr) return 0.0;

ret = ptr->hi - ptr->lo;
ret += total_width(ptr->ll);
ret += total_width(ptr->rr);

return ret;
}

答案 2 :(得分:0)

这是我刚刚在Haskell中编写的一个解决方案,下面是一个如何在解释器命令提示符中实现它的示例。段必须以元组列表[(a,a)]的形式呈现。我希望你能从代码中了解算法。

import Data.List

unionSegments segments = 
  let (x:xs) = sort segments
      one_segment = snd x - fst x
  in if xs /= []
        then if snd x > fst (head xs) 
                then one_segment - (snd x - fst (head xs)) + unionSegments xs
                else one_segment + unionSegments xs
        else one_segment

*主&GT; :加载“unionSegments.hs”
[1/1]编译Main(unionSegments.hs,解释)
好的,加载的模块:Main。
*主&GT; unionSegments [(5,8),(7,12)]
7

答案 3 :(得分:0)

Java实现

userId

公共类HelloWorld {

import  java.util.*;

}