比较指数搜索与二进制搜索

时间:2018-10-16 21:30:11

标签: algorithm search

除了空间复杂度之外,二进制搜索是否还胜过指数搜索?

1 个答案:

答案 0 :(得分:7)

这两种算法都在元素的有序列表中搜索值,但是它们解决了不同的问题。指数搜索是专门为无边界列表而设计的,而二进制搜索则处理有边界列表。

指数搜索的思想非常简单:搜索边界,然后然后执行二进制搜索。

示例

让我们举个例子。 A = [1, 3, 7, 8, 10, 11, 12, 15, 19, 21, 22, 23, 29, 31, 37]。此列表可以看作是二叉树(尽管无需构建树):

             15
        ____/  \____
       /            \
    __8__           _23__
   /     \         /     \
  3      11       21     31
 / \    /  \     /  \   /  \
1   7  10   12  19  22 29  37

二进制搜索

例如,对e = 27的二进制搜索将执行以下步骤

b0)假设T, R分别是树和树的根

                 15 (R)
            ____/  \____
           /            \
        __8__           _23__
       /     \         /     \
      3      11       21     31
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

b1)比较eRe > 15。假设T, R分别是T右子树及其根

                 15
            ____/  \____
           /            \
        __8__           _23_(R)
       /     \         /     \
      3      11       21     31
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

b2)比较eRe > 23。假设T, R分别是T右子树及其根

                 15
            ____/  \____
           /            \
        __8__           _23__
       /     \         /     \
      3      11       21     31 (R)
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

b3)比较eRe < 31。令T, R分别为T的左子树及其根

                 15
            ____/  \____
           /            \
        __8__           _23__
       /     \         /     \
      3      11       21     31___
     / \    /  \     /  \   /     \
    1   7  10   12  19  22 29 (R)  37

b4)比较eRe <> 29:该元素不在列表中,因为T没有子树。

指数搜索

例如e = 27的指数搜索将执行以下步骤

分别将T, R设为最左边的子树(即叶1)和其根(1

                 15
            ____/  \____
           /            \
        __8__           _23__
       /     \         /     \
      3      11       21     31
     / \    /  \     /  \   /  \
(R) 1   7  10   12  19  22 29  37

e1)比较eRe > 1。假设RR的父级,而T是以R为根的树

                 15
            ____/  \____
           /            \
        __8__           _23__
       /     \         /     \
  (R) 3      11       21     31 (R)
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

e2)比较eRe > 3。假设RR的父级,而T是以R为根的树:

                 15
            ____/  \____
           /            \
      (R)_8__           _23__
       /     \         /     \
      3      11       21     31 (R)
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

e3)比较eRe > 8。假设RR的父级,而T是以R为根的树:

             (R) 15
            ____/  \____
           /            \
        __8__           _23__
       /     \         /     \
      3      11       21     31 (R)
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

e4)比较eRe > 15R没有父母。假设TT的右子树,而R是其根:

                 15
            ____/  \____
           /            \
        __8__           _23_(R)
       /     \         /     \
      3      11       21     31
     / \    /  \     /  \   /  \
    1   7  10   12  19  22 29  37

e5..7)参见步骤b2..4)

时间复杂度

为了演示,让N = 2^nA 的大小,并让索引从1开始。如果N不是2的幂,则结果几乎相同。

0 <= i <= n为最小值,以使A[2^(i-1)] < e <= A[2^i](让A[2^-1] = -inf)成立。请注意,如果您有重复的值(即“最小值”),则这种间隔可能不是唯一的。

指数搜索

您需要进行i + 1次迭代才能找到i。 (在该示例中,您不断从子级跳到父级,直到找到大于e的父级或没有更多的父级为止)

然后,在选定的时间间隔上使用二进制搜索。此间隔的大小为2^i - 2^(i-1) = 2^(i-1)

在大小为2^k的数组中进行二进制搜索的成本是可变的:您可能会在第一次迭代中或在k迭代之后找到该值(根据元素,但基本上是在1k迭代之间,您无法事先知道)

在本例中,将j_i, 1 <= j_i <= i - 1设为二进制搜索所需的迭代次数(此间隔的大小为2^(i-1))。

二进制搜索

i最小,使A[2^(i-1)] < e <= A[2^i]。由于假设N = 2^n,二进制搜索将满足以下间隔:

我们从根A[2^(n-1)]开始。如果为e > A[2^(n-1)],则i = n因为R = A[2^(n-1)] < e < A[2^n]。否则,我们有e <= A[2^(n-1)]。如果为e > A[2^(n-2)],则为i = n-1,否则我们继续进行直到找到i

您需要n - i + 1的步骤才能使用二进制搜索找到i

  • 如果i = n在第一次迭代(e > R)时就知道,否则,请选择左侧的子树
  • 如果i = n-1,则需要两次迭代
  • 以此类推:如果为i = 0,则需要进行n次迭代。

然后,您需要进行j_i次迭代(如上所示)以完成搜索。

比较

如您所见,j_i迭代对于两种算法都是相同的。问题是:i + 1 < n - i + 1吗?即i < n - i2i < n?如果是,则指数搜索将比二进制搜索快。如果没有,则二进制搜索将比指数搜索快(或同样快)

让我们有所距离:2i < n等效于(2^i)^2 < 2^n2^i < sqrt(2^n)。在2^i < sqrt(N)期间,指数搜索速度更快。一旦2^i > sqrt(N),二进制搜索就会更快。请记住,e的索引小于或等于2^i,因为e <= A[2^i]

简单来说,如果您有N个元素,并且如果e位于前sqrt(N)个元素中,则指数搜索将更快,否则二进制搜索将更快。

这取决于分布,但是N - sqrt(N) > sqrt(N)是否为N > 4,因此二进制搜索可能比指数搜索快,除非您知道该元素将在第一个元素中一个或列表太短了

如果2^n < N < 2^(n+1)

我不会详细介绍,但这不会改变总体结论。

如果该值超出了2的最后一个乘方,则找到边界的指数成本已经是n+2,大于二进制搜索(小于或等于2^(n+1))。然后,您可能需要执行一个很小的时间间隔来执行二进制搜索,但是二进制搜索已经是赢家。

否则,您将值A[N]添加到列表中,直到获得2^(n+1)值为止。这不会改变指数搜索的任何内容,并且会减慢二进制搜索的速度。但是,如果e不在前sqrt(2^(n+1))个值中,这种缓慢的二进制搜索将保持更快的速度。

空间复杂度

这是一个有趣的问题,我不谈论指针的大小之类的事情。如果您要对到达的元素执行指数搜索并消耗它们(想象中的时间戳),则无需立即存储整个列表。您只需要存储一个元素(第一个),然后存储一个元素(第二个),然后存储两个元素(第三个和第四个),然后存储四个元素,然后是2^(i-1)个元素。如果i很小,则无需像常规二进制搜索中那样存储大列表。

实施

实施在这里真的不是问题。有关信息,请参见Wikipedia页面:Binary search algorithmExponential search

应用程序以及如何在两者之间进行选择

仅当序列无界或您知道该值可能在第一个序列中时,才使用指数搜索。无界:我喜欢时间戳的示例:它们严格地在增长。您可以想象一个带有存储时间戳的服务器。您可以要求n时间戳,并且正在寻找特定的时间戳。先询问1,然后询问2,然后询问4,然后询问8,...,并在一个时间戳超过您要查找的值时执行二进制搜索。

在其他情况下,请使用二进制搜索。

备注:指数搜索第一部分背后的思想有一些应用:

  • 在上限不受限制时猜测一个整数:尝试1、2、4、8、16 ...,并在超过该数字时缩小猜测范围(这是指数搜索);
  • 在大雾天里找到一座过河的桥:向左走100步。如果找不到桥梁,请返回到初始点,然后向右走200步。如果仍然找不到桥梁,请返回初始点,然后向左走400步。重复直到找到桥(或游泳);
  • TCP slow start中计算一个拥塞窗口:将发送的数据量加倍,直到出现拥塞为止。通常,TCP拥塞算法会更加谨慎,并且在算法的第二部分执行类似于线性搜索的操作,因为超出尝试次数会增加成本。