有效搜索已排序的数值

时间:2014-01-05 13:58:39

标签: java algorithm indexing

我有一个int[]数组,其中包含具有以下属性的值:

  • 已排序
  • 唯一(无重复)
  • 他们处于已知范围 [0..MAX)
  • MAX通常比阵列的长度(比如10-100x)大很多。
  • 有时数字在整个范围内均匀分布,但有时候连续数字序列很长。我估计这两种情况之间约为50/50。

鉴于此列表,我想有效地找到数组中特定值的索引(或者如果该值不存在,则找到下一个更高的值)。

我已经已经实现了一个直接二分搜索,其中间隔二分法运行得相当好,但我怀疑数据的性质/分布可以被利用来更快地收敛到解决方案。

我对优化平均案例搜索时间感兴趣,但重要的是最坏情况永远不会比O(log n)更糟,因为数组有时非常大。

问题:在普通情况下,有可能比纯二进制搜索做得更好吗?

编辑(澄清其他问题/评论)

  • O(log n)中的常量绝对重要。事实上,假设比O(log n)更好的算法复杂度是不可能的,那么常量可能只是唯一重要的东西......
  • 这通常是一次性搜索,所以虽然预处理是可能的,但它可能不值得。

4 个答案:

答案 0 :(得分:3)

这是在评论中,应该是一个答案。这是一项共同的努力,所以我将它作为CW答案:

您可能需要查看interpolation search。在最坏的情况下,他们可能O(log n)更糟糕,因此如果这是一个很难的要求,这将不适用。但是如果你的插值是合适的,根据数据分布,插值搜索可以击败直接二进制。

要知道,您必须使用相当智能的插值算法实现插值搜索,然后通过两者运行几个代表性数据集,以查看插值或二进制是否更适合。我认为它是两者中的一个,但我并不是真正的尖端搜索算法。

答案 1 :(得分:2)

我们在此处命名x的间隔,并在搜索的数字中命名z

由于您希望均匀分布值,因此可以使用插值搜索。这类似于二分搜索,但将索引范围拆分为start + ((z - x[start]) * (end - start)) / (x[end] - x[start])

要获得O(log n)的运行时间,您必须将插值搜索与二分搜索相结合(从二进制搜索开始,从交替插值搜索开始):

public int search(int[] values, int z) {
    int start = 0;
    int end = values.length-1;

    if (values[0] == z)
         return 0;
    else if (values[end] == z) {
        return end;
    }

    boolean interpolation = true;

    while (start < end) {
        int mid;
        if (interpolation) {
            mid = start + ((z - values[start]) * (end - start)) / (values[end] - values[start]);
        } else {
            mid = (end-start) / 2;
        }
        int v = values[mid];
        if (v == z)
            return mid;
        else if (v > z)
            end = mid;
        else
            start = mid;
        interpolation = !interpolation;
    }
    return -1;
}

由于while循环的每次迭代都在二进制搜索中执行一步,因此它最多使用二进制搜索将使用的迭代次数的两倍(O(log n))。由于每个第二步都是插值搜索的一步,如果输入具有所需的属性,算法应该快速减小intervall大小。

答案 2 :(得分:0)

如果int []是

  • 排序
  • 具有唯一值
  • 你知道范围(提前)

而不是搜索为什么不将值保存在其索引处。

假设数字是243而不是保存int [243] = 243中的值。

这样搜索将变得简单快捷。唯一剩下的就是找出更高的价值。

答案 3 :(得分:0)

我有一个解决方案 你说阵列可以是
1)数字在整个范围内均匀分布 2)有很长的连续数字序列。

所以,首先我们开始一个简单的测试,以确定它是type1还是type2 要测试类型1,
lenght = array.length;
range = array [length-1] - array [0];
现在考虑数字的值为
  {长度(1/5),长度(2/5),长度(3/5),长度(4/5)},
如果数组分布是类型1,那么我们大致知道数组[i]的值必须是什么,所以我们检查4个位置以上是否接近已知值,如果它的分布相等。
如果它们接近,那么它的分布相等,所以我们可以很容易地找到数组中的任何元素。如果我们找不到基于上述方法的元素,我们认为它是类型2。

如果上面的测试失败,那么它是类型2 ,这意味着在数组中很少有地方存在连续数字的长序列。

所以,我们用二分法搜索来解决它。解释如下 *我们首先在数组的中间搜索,(比如说长度为2,索引为i)

左= 0,右=长;
BEGIN
I =(左+右)/ 2;

案例a.1 :我们的搜索号大于数组[i]
左= I;
*现在我们检查那个位置是否存在任何长的连续序列,即array [i],array [i + 1],array [i + 2]是连续的int。

案例a.1.1 :(如果它们是连续的),
因为它们是连续的,并且序列可能很长,我们根据搜索整数值直接搜索特定索引 例如,如果我们的搜索int是10,并且序列是5,6,7,8,9,10,11 15,100,103,
和array [i] = 5,然后我们直接搜索数组[i + 10-5],
如果我们找到我们的搜索int,则返回它,否则只从案例a.2继续[因为它显然会小于它]通过设置为as作为
right =(array [i + 10-5])

案例a.1.2,如果不是连续的话 从BEGIN继续;

案例a.2:我们的搜索号小于数组[i],
*案例a.2与a.1完全相似 *类似检查是否有任何后序,即数组[i-2],数组[i-1],数组[i]顺序,
如果它们是连续的序列,请像我们在a.1.1中所做的那样搜索精确值 如果它们不连续,则重复类似于案例a.1.2。

案例a.3 ,这是我们的搜索int,
然后归还。

希望这有助于