jfe*_*ard 11
这两种算法都在有序的元素列表中搜索值,但它们解决了不同的问题。指数搜索是为无界列表明确设计的,而二分搜索则是针对有界列表。
指数搜索背后的想法非常简单:搜索边界,然后执行二分搜索。
让我们举个例子。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
Run Code Online (Sandbox Code Playgroud)
对e = 27
(例如)的二分搜索将经历以下步骤
b0) 设T, R
分别为树及其根
15 (R)
____/ \____
/ \
__8__ _23__
/ \ / \
3 11 21 31
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
B1)比较e
,以R
:e > 15
。让分别T, R
是T
右子树和它的根
15
____/ \____
/ \
__8__ _23_(R)
/ \ / \
3 11 21 31
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
B2)进行比较e
来R
:e > 23
。让分别T, R
是T
右子树和它的根
15
____/ \____
/ \
__8__ _23__
/ \ / \
3 11 21 31 (R)
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
B3)比较e
来R
:e < 31
。让我们T, R
来T
分别左子树和它的根
15
____/ \____
/ \
__8__ _23__
/ \ / \
3 11 21 31___
/ \ / \ / \ / \
1 7 10 12 19 22 29 (R) 37
Run Code Online (Sandbox Code Playgroud)
B4)比较e
到R
:e <> 29
:该元素是不是在列表中,因为T
没有树。
指数搜索e = 27
(例如)将经历以下步骤
让T, R
是最左边的子树(即,叶1
)和其根(1
分别)
15
____/ \____
/ \
__8__ _23__
/ \ / \
3 11 21 31
/ \ / \ / \ / \
(R) 1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
E1)比较e
来R
:e > 1
。我们R
是母公司R
和T
是具有树R
的根
15
____/ \____
/ \
__8__ _23__
/ \ / \
(R) 3 11 21 31 (R)
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
E2)进行比较e
来R
:e > 3
。我们R
是母公司R
和T
是具有树R
的根目录:
15
____/ \____
/ \
(R)_8__ _23__
/ \ / \
3 11 21 31 (R)
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
E3)进行比较e
来R
:e > 8
。我们R
是母公司R
和T
是具有树R
的根目录:
(R) 15
____/ \____
/ \
__8__ _23__
/ \ / \
3 11 21 31 (R)
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
E4)比较e
来R
:e > 15
。R
没有父母。让T
成为 的右子树T
并R
成为它的根:
15
____/ \____
/ \
__8__ _23_(R)
/ \ / \
3 11 21 31
/ \ / \ / \ / \
1 7 10 12 19 22 29 37
Run Code Online (Sandbox Code Playgroud)
e5..7) 见步骤 b2..4)
为了演示起见,让N = 2^n
是 的大小,A
让索引从 开始1
。如果N
不是二的幂,结果几乎一样。
让0 <= i <= n
成为最小值,使得A[2^(i-1)] < e <= A[2^i]
(let A[2^-1] = -inf
)。请注意,如果您有重复的值,这种间隔可能不是唯一的,因此是“最小值”。
您需要i + 1
迭代才能找到i
. (在这个例子中,你反复从孩子跳到父母,直到你发现父母大于e
或没有更多的父母)
然后在选定的时间间隔上使用二分搜索。这个区间的大小是2^i - 2^(i-1) = 2^(i-1)
。
在尺寸数组的二进制搜索的成本2^k
是可变的:你可能会在第一次迭代中找到该值,或之后k
的迭代(还有取决于元素的分布复杂的分析,但基本上,这是间1
和k
迭代,你可以事先不知道)
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
) else,您选择左子树i = n-1
,你需要两次迭代i = 0
,您将需要n
迭代。然后您将需要j_i
如上所示的迭代来完成搜索。
如您所见,这j_i
两种算法的迭代都是通用的。问题是:是i + 1 < n - i + 1
吗?即是i < n - i
或2i < n
?如果是,指数搜索将比二分搜索快。如果不是,二分搜索将比指数搜索快(或同样快)
让我们得到一些距离:2i < n
相当于(2^i)^2 < 2^n
or 2^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 页面:二元搜索算法和指数搜索。
仅当序列是无界的或当您知道该值可能是第一个时才使用指数搜索。无界:我喜欢时间戳的例子:它们正在严格增长。您可以想象一个存储时间戳的服务器。您可以要求n
时间戳,并且您正在寻找特定的时间戳。询问 1,然后是 2,然后是 4,然后是 8,...时间戳,并在一个时间戳超过您要查找的值时执行二进制搜索。
在其他情况下,使用二分查找。
备注:指数搜索的第一部分背后的思想有一些应用: