有间隙的二分搜索

Posted

技术标签:

【中文标题】有间隙的二分搜索【英文标题】:Binary search with gaps 【发布时间】:2016-08-08 12:17:42 【问题描述】:

让我们想象两个这样的数组: [8,2,3,4,9,5,7]

[0,1,1,0,0,1,1]

我怎样才能只对下面有 1 的数字执行二进制搜索,而忽略其余部分? 我知道这可以在 O(log n) 比较中进行,但我当前的方法较慢,因为它必须遍历所有 0 直到达到 1。

【问题讨论】:

我不是那个意思。我想对第一个数组进行二进制搜索,但只对在第二个数组的同一索引中具有 1 的数字,忽略其余部分,因此其余部分是否排序无关紧要。 二分查找本身是 O(log n),但它需要对输入数组进行排序。排序可以在 O(n * log n) 中完成,过滤第二个数组中与 1 相关的值可以在 O(n) 中完成。因此,整体算法将是 O(n * log n)。使用二分搜索,您再好不过了。 我会换一种方式解释:这里是和之前一样的例子:[8,2,3,4,9,5,7] [0,1,1,0,0,1 ,1] 如果我们忽略第一个数组中下面有 0 的数字,第一个数组看起来像这样 [2,3,5,7] 它已排序,所以我们可以进行二分查找。目标是忽略其余数字 【参考方案1】:

如果您点击下方带有 0 的数字,则需要在两个方向上扫描下方带有 1 的数字,直到找到它 - 否则本地搜索空间已用尽。由于对 1 的扫描是线性的,因此 0 与 1 的比率决定了生成的算法是否仍能比线性更快。

【讨论】:

O(n) 最差时间复杂度对于二分搜索没有意义。没有更有效的方法吗? 好吧,如果你只有 log(n) 个零,我认为它仍然是 O(log n)。如果您有更多的零,则搜索零将占主导地位,您将更接近线性时间。问题是带 0 的值没有排序,所以不能使用二分查找跳过它们。因此,如果太多,跳过它们将占总时间。 这是个大问题。我正在尝试制作一个仅进行 n 次交换的 O(nlogn) 排序算法。我需要进行 n 次二进制搜索,所以如果在最坏的情况下每次搜索都是 O(n)(一切都是 0),那么它的性能将接近于 O(n 平方)。我正在做更多的研究,也许我需要另一种数据结构。 那么也许您应该在问题中陈述完整的原始问题?【参考方案2】:

这个问题很老了,但我刚刚发现了一个绝妙的小技巧,可以在大多数情况下解决这个问题。我正在写这个答案,以便我可以在其他地方参考它:

排序数组中的快速追加、删除和二分查找


需要从排序的集合中动态插入或删除项目,同时保留搜索能力,这通常迫使我们从使用二分搜索的简单数组表示切换到某种搜索树——一种更复杂的数据结构.

但是,如果您只需要在末尾插入(即始终插入最大或最小的项目),或者根本不需要插入,则可以使用更简单的数据结构。它包括:

    一个动态的(可调整大小的)项目数组,项目数组;和 整数的动态数组,set 数组。集合数组用作disjoint set data structure,使用此处描述的单数组表示:How to properly implement disjoint set data structure for finding spanning forests in Python?

这两个数组的大小始终相同。只要没有删除,item数组就只包含排序好的item,而set数组中充满了与这些item对应的单例集。

但是,如果项目已被删除,则项目数组中的项目只有在集合数组中的相应位置存在根集合时才有效。所有合并到一个根的集合在集合数组中都是连续的。

此数据结构支持如下所需的操作:

追加 (O(1))

要追加一个新的最大项,只需将该项追加到项数组中,然后将一个新的单例集合追加到集合数组中。

删除(有效摊销 O(log N))

要删除一个有效项,首先调用 search 找到相邻的较大的有效项。如果没有更大的有效项目,则只需截断两个数组以删除该项目和所有相邻的已删除项目。由于合并的集合在集合数组中是连续的,这将使两个数组保持一致的状态。

否则,合并集合数组中已删除项和相邻项的集合。如果已删除项的集合被选为新根,则将相邻项移动到项数组中已删除项的位置。从现在开始,任何未选择的位置都将不再使用,并且可以在必要时取消以释放引用。

如果删除后只有不到一半的项数组有效,则应从项数组中删除已删除的项,并将集合数组重置为全单态。

搜索(有效摊销 O(log N))

二分查找正常进行,只是我们需要找到每个测试位置的代表项:

int find(item_array, set_array, itemToFind) 
    int pos = 0;
    int limit = item_array.length;
    while (pos < limit) 
        int testPos = pos + floor((limit-pos)/2);
        if (item_array[find_set(set_array, testPos)] < itemToFind) 
            pos = testPos + 1; //testPos is too low
         else 
            limit = testPos; //testPos is not too low
        
    
    if (pos >= item_array.length) 
        return -1;  //not found
    
    pos = find_set(set_array, pos);
    return (item_array[pos] == itemToFind) ? pos : -1;

【讨论】:

如果您看过这是文献,请发表评论。

以上是关于有间隙的二分搜索的主要内容,如果未能解决你的问题,请参考以下文章

关于二分搜索 简单左侧区间右侧区间

二分搜索树(Binary Search Tree)

使用二分搜索查找多个条目

二分查找法(二分搜索法)

数据结构之二分搜索树

二分搜索与线性搜索奇怪的时间