C++ 比二进制搜索有序列表更快

C++ 比二进制搜索有序列表更快,c++,arrays,algorithm,search,binary-search,C++,Arrays,Algorithm,Search,Binary Search,有没有比二进制搜索更快的算法来搜索数组的排序值 在我的例子中,我在a数组中有一个排序的值(可以是任何类型的值),如果我正在查找的值在a[n]和[n+1]是和否的范围内,我需要返回n。是的,平均而言,有些搜索比对分搜索更快。但我相信它们仍然是O(lgn),只是有一个较低的常数 您希望尽可能减少查找元素所需的时间。通常,希望使用较少的步骤,实现这一点的一种方法是最大限度地增加在每个步骤中消除的元素的预期数量。使用二分法时,总是正好消除一半的元素。如果您对元素的分布有所了解,您可以做得更好。但是,选择

有没有比二进制搜索更快的算法来搜索数组的排序值


在我的例子中,我在
a
数组中有一个排序的值(可以是任何类型的值),如果我正在查找的值在
a[n]和[n+1]
是和否的范围内,我需要返回
n
。是的,平均而言,有些搜索比对分搜索更快。但我相信它们仍然是O(lgn),只是有一个较低的常数

您希望尽可能减少查找元素所需的时间。通常,希望使用较少的步骤,实现这一点的一种方法是最大限度地增加在每个步骤中消除的元素的预期数量。使用二分法时,总是正好消除一半的元素。如果您对元素的分布有所了解,您可以做得更好。但是,选择分区元素的算法通常比选择中点更复杂,而且这种额外的复杂性可能会压倒您希望通过使用更少的步骤所节省的任何时间

实际上,在这样的问题中,攻击二阶效应(如缓存局部性)比搜索算法更好。例如,在重复进行二进制搜索时,同样的几个元素(第一、第二和第三个四分位数)被频繁使用,因此将它们放在单个缓存线中可能比随机访问列表要好得多

将每个级别划分为4个或8个相等的部分(而不是2个)并对这些部分进行线性搜索也可能比对分搜索快,因为线性搜索不需要计算分区,并且具有较少的数据依赖性,从而导致缓存暂停


但所有这些仍然是O(lgn)。

您可以将它们放在哈希表中,然后搜索将是O(1)。但这会占用大量内存,如果您继续添加项,则可能需要重新调整哈希表。再扣税为O(n),但将摊销至O(1)。这主要取决于您是否能够承受该空间和潜在的缓存未命中。

如果列表中的值均匀分布,则您可以尝试加权拆分,而不是二进制拆分,例如,如果所需值是从当前下限到当前值的三分之一,则您可以尝试也是三分之一的元素。但是,在值聚集的列表中,这可能会受到严重影响。

一种可能性是将其视为查找函数的根。基本上,发现:

a[i] <= i <= a[i + 1]

a[i]在二进制搜索中,您将列表拆分为两个“子列表”,并且只搜索可能包含该值的子列表。根据阵列的大小,如果将阵列拆分为两个以上的拼接,则可以看到加速效果

通过保留索引,您可以确定必须首先搜索数组的哪个区域。就像在一个大城市的电话簿里,你可以从外面看到,你必须从那里开始搜索。(我很难在文本中表达我的想法,我还没有找到一个更好的英文链接来解释它)。

如果值是整数,你可以做得比O(logn)更好,在这种情况下,你可以实现的最坏情况下的最佳运行时间,就n而言,是O(sqrt(logn))。否则,除非在输入序列中有模式,否则就没有办法击败O(logn)。在整数的情况下,有两种方法用于拍O(对数n)

首先,您可以使用y-fast树,它通过在哈希表中存储所有前缀来工作,对于这些前缀,您至少要存储一个带有该前缀的整数。这使您能够执行二进制搜索以查找最长匹配前缀的长度。这使您能够在时间O(log w)中查找要搜索的元素的后续元素,其中w是字中的位数。尽管有一些细节需要处理,以使其工作并仅使用线性空间,但它们并不太糟糕(请参见下面的链接)

其次,您可以使用融合树,它使用位技巧使您能够在恒定数量的指令中执行w^O(1)比较,从而产生O(logn/logw)的运行时间

当logw=sqrt(logn)时,这两个数据结构之间会出现最佳的折衷,运行时间为O(sqrt(logn))


有关上述内容的详细信息,请参见Erik Demaine课程的第12课和第13课:

首先,在进行优化之前进行测量

你真的需要优化搜索吗

如果是这样,那么第二,首先考虑算法的复杂性。例如,你能用树(比如说
std::map
)代替数组吗?如果是这样,则取决于插入/删除与搜索的相对频率,但手头有一个排序数组的前提表明,与数据集更改相比,搜索是频繁的,因此为插入/删除做一些额外的工作是有意义的,使每次搜索更快——即对数时间

如果您发现搜索时间确实是一个需要解决的瓶颈,并且不可能改变数据表示,而且列表很短,那么线性搜索通常会更快,因为每次比较所做的工作更少

否则,如果列表较长,并且不知道或假设值的特定分布,并且这些值不能被视为数字,并且内存消耗应该是恒定的(例如,排除构建哈希表的可能性),然后,二进制搜索在每次比较中产生1位信息,这可能是第一次搜索所能做的最好的


干杯&hth.

如果你有大量的数字要查找,而且碰巧它们也被排序了,你可以在O(n+m)中进行排序,其中m是要查找的数字的数量。基本上只是您典型的合并算法,只需稍加修改即可记录每个选中的数字之前将插入的值(如果需要)
a[i] - i <= 0 <= a[i + 1] - i
int exponential_search(int A[], int key)
{
  // lower and upper bound for binary search
  int lower_bound = 0;
  int upper_bound = 1;

  // calculate lower and upper bound
  while (A[upper_bound] < key) {
    lower_bound = upper_bound;
   upper_bound = upper_bound * 2;
  }
  return binary_search(A, key, lower_bound, upper_bound);
}