用于查找包含单元的一组区域的算法

时间:2010-10-28 04:25:43

标签: algorithm excel vba spreadsheet regions

我正在使用一些电子表格数据,我有一组具有任意界限的单元格区域。给定任何单元格,确定包含单元格的区域子集的最快方法是什么?

目前,我最好的方法是对主要排序字段为区域的起始行索引,后跟其结束行索引,起始列索引,然后结束列索引的区域进行排序。当我想基于给定的单元格进行搜索时,我将二进制搜索到第一个区域,其起始行索引位于单元格的行索引之后,然后检查所有区域,然后检查它们是否包含单元格,但这太慢了

2 个答案:

答案 0 :(得分:1)

基于一些谷歌搜索,这是二维点封闭搜索问题或“刺伤问题”的一个例子。参见:

http://www.cs.nthu.edu.tw/~wkhon/ds/ds10/tutorial/tutorial6.pdf

这里(从第21/52页开始):

http://www.cs.brown.edu/courses/cs252/misc/slides/orthsearch.pdf

涉及的关键数据结构是分段树:

http://en.wikipedia.org/wiki/Segment_tree

对于2-D情况,看起来您可以构建包含分段树的分段树并获得O(log ^ 2(n))查询复杂度。 (我认为您当前的解决方案是O(n),因为平均而言,您只需用二分搜索排除一半区域。)

但是,你说“电子表格”,这意味着你可能有一个相对较小的领域可以使用。更重要的是,你有整数坐标。你说“最快”,这意味着你可能愿意交换空间和设置时间以加快查询速度。

你没有说出哪个电子表格,但下面的代码是一个非常低效,但是简单,蛮力的二维查找表的Excel / VBA实现,一旦设置,就有O(1 )查询复杂性:

Public Sub brutishButShort()
    Dim posns(1 To 65536, 1 To 256) As Collection

    Dim regions As Collection
    Set regions = New Collection

    Call regions.Add([q42:z99])
    Call regions.Add([a1:s100])
    Call regions.Add([r45])

    Dim rng As Range
    Dim cell As Range
    Dim r As Long
    Dim c As Long

    For Each rng In regions
        For Each cell In rng
            r = cell.Row
            c = cell.Column

            If posns(r, c) Is Nothing Then
                Set posns(r, c) = New Collection
            End If

            Call posns(r, c).Add(rng)
        Next cell
    Next rng

    Dim query As Range
    Set query = [r45]

    If Not posns(query.Row, query.Column) Is Nothing Then
        Dim result As Range
        For Each result In posns(query.Row, query.Column)
            Debug.Print result.address
        Next result
    End If
End Sub

如果您需要担心更大的网格或相对于网格较大的区域,则可以使用两个1-D查找表来节省大量空间和设置时间。但是,您有两个查找,并且需要获取两个结果集的交集。

答案 1 :(得分:0)

我认为你想确定单元格和区域的相交是否为Nothing

Sub RegionsContainingCell(rCell As Range, ParamArray vRegions() As Variant)

    Dim i As Long

    For i = LBound(vRegions) To UBound(vRegions)
        If TypeName(vRegions(i)) = "Range" Then
            If Not Intersect(rCell, vRegions(i)) Is Nothing Then
                Debug.Print vRegions(i).Address
            End If
        End If
    Next i

End Sub

Sub test()

    RegionsContainingCell Range("B50"), Range("A1:Z100"), Range("C2:C10"), Range("B1:B70"), Range("A1:C30")

End Sub