是否可以有效地计算与数轴上单个点 P 重叠的线段数?

Posted

技术标签:

【中文标题】是否可以有效地计算与数轴上单个点 P 重叠的线段数?【英文标题】:Is it possible to efficiently count the number of line segments that overlap a single point P on a number line? 【发布时间】:2013-06-22 19:34:59 【问题描述】:

是否可以有效地计算与数轴上单个点P 重叠的线段数?

所有线段都位于一条数字线上(它是1-D 世界,而不是3-D 世界)。

每条线段都有一个起点坐标X1和一个终点坐标X2

例子:

Line segment A spans from X1==1 to X2==3
Line segment B spans from X1==2 to X2==4
Line segment C spans from X1==3 to X2==5
Line segment D spans from X1==1 to X2==4
----------------------------------------
Ex1: Line segments that overlap point P==2: A,B and D   >>> overlap count==3.
Ex2: Line segments that overlap point P==7: None        >>> overlap count==0.
Ex3: Line segments that overlap point P==3: A,B,C and D >>> overlap count==4.

当然,如果只有4条线段,那么代码就简单了。但是,如果有一个庞大的 4 亿条线段的空间数据库,那么搜索就会很慢。

是否有任何算法可以有效地搜索此线段列表中的重叠总数?

我目前看到的内容

有关空间索引搜索算法的文章。 Interval trees(看起来很有希望)。 Segment trees(看起来很有希望)。 RTrees。

【问题讨论】:

您是否只需要生成一次重叠列表,还是希望为随机点计算此值? @Floris 我必须非常快地计算很多随机点。我可能想输入 100,000 个点,并为每个点生成重叠线段的数量。 @Mathfan 只是(自然)浮点整数? @Armin 目前只需要支持自然整数即可。 是否允许对查询点进行排序和对区间进行排序?每个查询点的区间数分布如何? 【参考方案1】:

如果您按起始值对列表进行排序,然后再次(对于相同的起始值)按长度排序,您最终会得到高效算法的根。

sort the list by starting value
for the same starting value, sort by length (longest first)

然后,当您需要与给定点 P 重叠的线段数时:

for a given value p
find the points in the list with starting value <= p (binary search - fast)
for each starting value, start with the longest length
if it spans the point of interest, increment counter
if not, go to the next smaller start value
keep going until you have reached the smallest starting value

这并不完美,但比搜索 1000 万个点要好得多(虽然初始排序显然需要一些时间。但你只需要这样做一次)。

【讨论】:

如果我正确地计算了运行时间,这个算法有 m * (n/2 + nlog(n) ) 其中 m 是查询点的数量,如果我是对的那么它很慢,请更正如果我错了。 m*n log(n)n^2 log (n) 快​​假设 m&lt;n... 不是吗? 也许我错过了一些东西,但是: 1. nlog(n) 在开始时一次 2. 对每个查询点进行二进制搜索 O(log n) 3. 从二进制后收到的位置开始扫描段数组搜索直到 p 【参考方案2】:

对查询点和区间端点进行递增排序,在一个数组中;对于每个点,保留一个标志,告诉它是间隔开始、间隔结束还是查询。

将计数器初始化为零并扫描列表。开始增加计数器;结束减少它;查询通过读取计数器知道重叠间隔的数量。

如果可以使用特殊排序,时间 (N+M).Log(N+M) 或更好。


如果不允许对查询点进行排序,只需对区间端点进行排序。在单次线性扫描中,您可以计算每个端点之后的重叠数。

对于给定的查询点,您通过二分法搜索找到相关端点,因此重叠计数。

M.Log(M)+N.Log(M) 用于 M 个区间和 N 个查询点。


如果不允许对区间进行排序,只需对查询点进行排序即可。

依次处理每个区间,通过二分查找找到它重叠的第一个查询点,并增加它重叠的所有查询点的计数器。

N.Log(N)+M.Log(N)+O 其中 O 是间隔/查询重叠的总数。


如果您根本不允许排序,请针对每个时间间隔对每个查询进行详尽的测试,N.M.

【讨论】:

是的,如果对所有点进行排序:开始点、结束点和查询点,那么算法有 n^2 log(n),这还不错。谢谢。 @dmgcodevil:无法理解您的说法,但 N²Log(N) 的复杂性很糟糕。 不,不是 n^2 log(n),而是 m+ mlog(m) 其中 m 是开始点、结束点和查询点的总和:m = s+e+q。尽管如此,这并不是解决这个问题的最糟糕的解决方案。【参考方案3】:

看看区间树或分段树来帮助解决这类问题。 This answer 有一些很好的例子来说明这些技术如何帮助您。

【讨论】:

【参考方案4】:

首先要意识到你不能做得比 O(N) 更好,因为你需要至少查看每个线段一次。(其中 N = 线段数)

让我们有一个数组 Line_segments_at,它存储通过每个点的线段数。

首先我们需要将此数组初始化为 0。 然后,当我们查看第 i 条线段时,我们需要这样做:

for(int j=x1[i];j<=x2[i];j++)
 Line_segments_at[j]++;

对于每个查询点 k,我们可以简单地将结果返回为 Line_segments_at[k]。

【讨论】:

我可以做得比 O(n) 好得多,甚至比 log(n) 还要好。一旦你构建了一棵树,它可以在unsigned int 的整个范围内对任意数量的行进行最多 32 次迭代。 @Armin:我不知道,请发布答案。 @user1944441:这取决于空间索引是否可以创建一次然后重复使用多次,或者是否也将使用一次。这类似于排序和执行二进制搜索的情况。当然搜索的复杂度为 O(logN),但首先您必须对元素进行排序或构建具有 O(NlogN) 的树。所以如果你只想做一次,简单的 O(N) 方法会更快。

以上是关于是否可以有效地计算与数轴上单个点 P 重叠的线段数?的主要内容,如果未能解决你的问题,请参考以下文章

数轴上表示整数的点称为整点.某数轴的单位长度是1厘米,若在这个数轴上随意画出一条长为2008厘米的线段AB

数轴上表示整数的点称为整点.某数轴上的单位长度是1cm,若在这个数轴上随意画出一条长2014cm的线段AB,

Vijos P1103 校门外的树线段树,模拟

D 洛谷 P3602 Koishi Loves Segments [贪心 树状数组+堆]

little w and Segment Coverage

点与线线与线之间的位置关系