Draft:二分搜尋

来自testwiki
imported>深鸣2025年2月13日 (四) 15:24的版本 (深鸣移动页面User:深鸣/二分查找算法Draft:二分搜尋
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到导航 跳转到搜索

Template:NoteTA Template:About Template:Infobox algorithm Template:地区用词Template:Efn是用于查找Template:Le中目标值位置的搜索算法Template:Sfn[1]

In computer science, binary search, also known as half-interval search, logarithmic search, or binary chop, is a search algorithm that finds the position of a target value within a sorted array. Binary search compares the target value to the middle element of the array. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half, again taking the middle element to compare to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array.

Binary search runs in logarithmic time in the worst case, making O(logn) comparisons, where n is the number of elements in the array.Template:Efn[2] Binary search is faster than linear search except for small arrays. However, the array must be sorted first to be able to apply binary search. There are specialized data structures designed for fast searching, such as hash tables, that can be searched more efficiently than binary search. However, binary search can be used to solve a wider range of problems, such as finding the next-smallest or next-largest element in the array relative to the target even if it is absent from the array.

There are numerous variations of binary search. In particular, fractional cascading speeds up binary searches for the same value in multiple arrays. Fractional cascading efficiently solves a number of search problems in computational geometry and in numerous other fields. Exponential search extends binary search to unbounded lists. The binary search tree and B-tree data structures are based on binary search.

算法

二分查找适用于有序数组。二分查找首先比较数组中间的元素与目标值。如果目标值与该元素匹配,则返回该元素在数组中的位置;如果目标值小于该元素,则在数组较小的那一半中继续查找;如果目标值大于该元素,则在数组较大的那一半中继续查找。通过这种方法,每次迭代都能将搜索范围缩小一半。Template:Sfn

过程

给定一个包含n个元素的数组A,其中的值或记录分别为A0,A1,A2,,An1,且满足A0A1A2An1。假设目标值为T。下面的子程序使用二分查找来寻找T在数组A中的索引。Template:Sfn

  1. L0Rn1
  2. 如果L>R,则搜索失败并终止。
  3. m(中间元素的位置)为L+R2向下取整值,即不大于L+R2的最大整数。
  4. 如果Am<T,则取Lm+1,并返回步骤2。
  5. 如果Am>T,则取Rm1,并返回步骤2。
  6. 如果Am=T,则搜索完成,返回m

这个迭代过程使用两个变量LR来跟踪搜索边界。该过程可以用伪代码表示如下,其中变量名和类型与上文相同,floor下取整函数unsuccessful表示搜索失败时的特定返回值:Template:Sfn

二分查找过程
function binary_search(A, n, T) is
    L := 0
    R := n − 1
    while L ≤ R do
        m := floor((L + R) / 2)
        if A[m] < T then
            L := m + 1
        else if A[m] > T then
            R := m − 1
        else:
            return m
    return unsuccessful

也可取mL+R2向上取整值。如此所做,若目标值在数组中出现多次,结果可能会有所不同。

替代过程

上述过程中,每次迭代都会检查中间元素(m)是否等于目标值(T)。而在其他一些实现中,仅剩下一个元素(即L=R)时,才会执行这项检查,这样每次迭代时就无需检查。这种方式的比较循环更快,因为每次迭代少了一次比较,但平均只需要多一次迭代。[3]

Template:Le于1962年首次发表了省略此检查的实现。[3]Template:Sfn

  1. L0Rn1
  2. LR时,
    1. m(中间元素的位置)为L+R2向上取整值,即不小于L+R2的最小整数。
    2. 如果Am>T,取Rm1
    3. 否则说明AmT,取Lm
  3. 现在L=R,搜索完成。如果AL=T,返回L。否则,搜索失败并终止。

该版本的伪代码如下,其中ceil是上取整函数:

function binary_search_alternative(A, n, T) is
    L := 0
    R := n − 1
    while L != R do
        m := ceil((L + R) / 2)
        if A[m] > T then
            R := m − 1
        else:
            L := m
    if A[L] = T then
        return L
    return unsuccessful

重复元素

即使数组中存在重复元素,算法可能返回任意一个与目标值相等的索引。例如,如果要搜索的数组为[1,2,3,4,4,5,6,7],且目标值为4,那么算法返回第4个(索引为 3)或第5个(索引为4)元素都是正确的。常规过程通常返回第4个元素(索引为3),但并不总是返回第一个重复项(考虑数组[1,2,4,4,4,5,6,7],这时依然返回第4个元素)。然而,有时需要找到目标值在数组中重复出现的最左侧或最右侧的元素。在上述例子中,第4个元素是值为4的最左侧元素,而第5个元素是值为4的最右侧元素。上述的替代过程总是会返回最右侧元素的索引(如果该元素存在的话)。Template:Sfn

查找最左侧元素的过程

要查找最左边的元素,可以使用以下过程:Template:Sfn

  1. L0Rn
  2. L<R时,
    1. m(中间元素的位置)为L+R2的向下取整值,即不大于L+R2的最大整数。
    2. 如果Am<T,取Lm+1
    3. 否则说明AmT,取Rm
  3. 返回L

如果L<nAL=T,那么AL是等于T的最左侧元素。即使T不在数组中,L也是T在数组中的排序位置,即数组中小于T的元素数量。

该版本的伪代码如下,其中floor是下取整函数:

function binary_search_leftmost(A, n, T):
    L := 0
    R := n
    while L < R:
        m := floor((L + R) / 2)
        if A[m] < T:
            L := m + 1
        else:
            R := m
    return L

查找最右侧元素的过程

要查找最右边的元素,可以使用以下过程:Template:Sfn

  1. L0Rn
  2. L<R时,
    1. m(中间元素的位置)为L+R2的向下取整值,即不大于L+R2的最大整数。
    2. 如果Am>T,取Rm
    3. 否则说明AmT,取Lm+1
  3. 返回R1

如果R>0AR1=T,那么AR1是等于T的最右侧元素。即使T不在数组中,nR也是数组中大于T的元素数量。

该版本的伪代码如下,其中floor是下取整函数:

function binary_search_rightmost(A, n, T):
    L := 0
    R := n
    while L < R:
        m := floor((L + R) / 2)
        if A[m] > T:
            R := m
        else:
            L := m + 1
    return R - 1

近似匹配

二分查找可计算近似匹配。上图中,目标值5本身不在数组中,但通过近似匹配,能计算出其排序位置、前驱、后继、最近邻

上述过程仅用于精确匹配,即找到目标值的位置。然而,由于二分查找在有序数组上进行,所以很容易扩展它以执行近似匹配。例如,二分查找可以用来计算给定值的排序位置(即比它小的元素的数量)、前驱(前一个较小的元素)、后继(下一个较大的元素)、最近邻Template:Le(查找两个值之间的元素数量)可以通过查询两次排序位置来完成。Template:Sfn

  • 查询排序位置可以使用查找最左侧元素的过程来完成。程序的返回值即为小于目标值的元素数量。Template:Sfn
  • 查询前驱可以通过查询排序位置来执行。如果目标值的排序位置为r,那么其前驱的位置为r1Template:Sfn
  • 对于后继查询,可以查找最右侧元素。如果得到的结果为r,那么目标值的后继位置就是r+1Template:Sfn
  • 目标值的最近邻是其前驱或后继之一,取决于哪个位置更接近。
  • 区间查询也很简单。Template:Sfn一旦知道了两个值的位置,区间内大于等于第一个值且小于第二个值的元素数量就是两个位置之差。考虑到是否需要将区间的端点包含在内,以及数组中是否包含与端点匹配的元素,其结果可能会增加或减少1。Template:Sfn

性能

Template:Hatnote

表示二分查找的。这里要查找的数组是[20,30,40,50,80,90,100],目标值是40
最坏情况下,搜索到达树的最深层;而最好情况是目标值正好为中间元素

就比较次数而言,可以将二分查找的过程放在二叉树上执行,以分析其性能。树的根节点是数组的中间元素。左半部分的中间元素是根节点的左子节点,右半部分的中间元素是根节点的右子节点,其余部分以类似方式构建。搜索过程从根节点开始,根据目标值是小于还是大于当前节点的值来选择遍历左子树还是右子树。[2]Template:Sfn

最坏情况下,二分查找需要比较log2(n)+1次。此时搜索会达到树的最深层,并且对于任何二分查找,树的层数总为log2(n)+1。若目标元素不在数组中,可能会发生最坏情况:若n可以表示为2的某次幂减1,那么查找过程总会遍历到最深层,一定会发生最坏情况;否则,搜索过程可能会在倒数第二层中止,此时比较了log2(n)次,比最坏情况少一次。Template:Sfn

平均情况下,当目标元素在数组中时,二分查找的比较次数是log2(n)+1(2log2(n)+1log2(n)2)/n(假设每个元素被搜索的概率相等),近似于log2(n)1;若目标元素不在数组中,二分查找的比较次数平均为log2(n)+22log2(n)+1/(n+1)(假设范围内及范围外的元素被搜索的概率相等)。Template:Sfn

最好情况下,即目标值正好是数组的中间元素,二分查找在一次比较后就能返回其位置。Template:Sfn

In terms of iterations, no search algorithm that works only by comparing elements can exhibit better average and worst-case performance than binary search. The comparison tree representing binary search has the fewest levels possible as every level above the lowest level of the tree is filled completely.Template:Efn Otherwise, the search algorithm can eliminate few elements in an iteration, increasing the number of iterations required in the average and worst case. This is the case for other search algorithms based on comparisons, as while they may work faster on some target values, the average performance over all elements is worse than binary search. By dividing the array in half, binary search ensures that the size of both subarrays are as similar as possible.Template:Sfn

空间复杂度

Binary search requires three pointers to elements, which may be array indices or pointers to memory locations, regardless of the size of the array. Therefore, the space complexity of binary search is O(1) in the word RAM model of computation.

平均情况的推导

The average number of iterations performed by binary search depends on the probability of each element being searched. The average case is different for successful searches and unsuccessful searches. It will be assumed that each element is equally likely to be searched for successful searches. For unsuccessful searches, it will be assumed that the intervals between and outside elements are equally likely to be searched. The average case for successful searches is the number of iterations required to search every element exactly once, divided by n, the number of elements. The average case for unsuccessful searches is the number of iterations required to search an element within every interval exactly once, divided by the n+1 intervals.Template:Sfn

成功的搜索

In the binary tree representation, a successful search can be represented by a path from the root to the target node, called an internal path. The length of a path is the number of edges (connections between nodes) that the path passes through. The number of iterations performed by a search, given that the corresponding path has length l, is l+1 counting the initial iteration. The internal path length is the sum of the lengths of all unique internal paths. Since there is only one path from the root to any single node, each internal path represents a search for a specific element. If there are n elements, which is a positive integer, and the internal path length is I(n), then the average number of iterations for a successful search T(n)=1+I(n)n, with the one iteration added to count the initial iteration.Template:Sfn

Since binary search is the optimal algorithm for searching with comparisons, this problem is reduced to calculating the minimum internal path length of all binary trees with n nodes, which is equal to:Template:Sfn

I(n)=k=1nlog2(k)

For example, in a 7-element array, the root requires one iteration, the two elements below the root require two iterations, and the four elements below require three iterations. In this case, the internal path length is:Template:Sfn

k=17log2(k)=0+2(1)+4(2)=2+8=10

The average number of iterations would be 1+107=237 based on the equation for the average case. The sum for I(n) can be simplified to:Template:Sfn

I(n)=k=1nlog2(k)=(n+1)log2(n+1)2log2(n+1)+1+2

Substituting the equation for I(n) into the equation for T(n):Template:Sfn

T(n)=1+(n+1)log2(n+1)2log2(n+1)+1+2n=log2(n)+1(2log2(n)+1log2(n)2)/n

For integer n, this is equivalent to the equation for the average case on a successful search specified above.

失败的搜索

Unsuccessful searches can be represented by augmenting the tree with external nodes, which forms an extended binary tree. If an internal node, or a node present in the tree, has fewer than two child nodes, then additional child nodes, called external nodes, are added so that each internal node has two children. By doing so, an unsuccessful search can be represented as a path to an external node, whose parent is the single element that remains during the last iteration. An external path is a path from the root to an external node. The external path length is the sum of the lengths of all unique external paths. If there are n elements, which is a positive integer, and the external path length is E(n), then the average number of iterations for an unsuccessful search T(n)=E(n)n+1, with the one iteration added to count the initial iteration. The external path length is divided by n+1 instead of n because there are n+1 external paths, representing the intervals between and outside the elements of the array.Template:Sfn

This problem can similarly be reduced to determining the minimum external path length of all binary trees with n nodes. For all binary trees, the external path length is equal to the internal path length plus 2n.Template:Sfn Substituting the equation for I(n):Template:Sfn

E(n)=I(n)+2n=[(n+1)log2(n+1)2log2(n+1)+1+2]+2n=(n+1)(log2(n)+2)2log2(n)+1

Substituting the equation for E(n) into the equation for T(n), the average case for unsuccessful searches can be determined:Template:Sfn

T(n)=(n+1)(log2(n)+2)2log2(n)+1(n+1)=log2(n)+22log2(n)+1/(n+1)

另一过程的性能

Each iteration of the binary search procedure defined above makes one or two comparisons, checking if the middle element is equal to the target in each iteration. Assuming that each element is equally likely to be searched, each iteration makes 1.5 comparisons on average. A variation of the algorithm checks whether the middle element is equal to the target at the end of the search. On average, this eliminates half a comparison from each iteration. This slightly cuts the time taken per iteration on most computers. However, it guarantees that the search takes the maximum number of iterations, on average adding one iteration to the search. Because the comparison loop is performed only log2(n)+1 times in the worst case, the slight increase in efficiency per iteration does not compensate for the extra iteration for all but very large n.Template:EfnTemplate:Sfn[4]

运行时间和缓存使用

In analyzing the performance of binary search, another consideration is the time required to compare two elements. For integers and strings, the time required increases linearly as the encoding length (usually the number of bits) of the elements increase. For example, comparing a pair of 64-bit unsigned integers would require comparing up to double the bits as comparing a pair of 32-bit unsigned integers. The worst case is achieved when the integers are equal. This can be significant when the encoding lengths of the elements are large, such as with large integer types or long strings, which makes comparing elements expensive. Furthermore, comparing floating-point values (the most common digital representation of real numbers) is often more expensive than comparing integers or short strings.[5]

On most computer architectures, the processor has a hardware cache separate from RAM. Since they are located within the processor itself, caches are much faster to access but usually store much less data than RAM. Therefore, most processors store memory locations that have been accessed recently, along with memory locations close to it. For example, when an array element is accessed, the element itself may be stored along with the elements that are stored close to it in RAM, making it faster to sequentially access array elements that are close in index to each other (locality of reference). On a sorted array, binary search can jump to distant memory locations if the array is large, unlike algorithms (such as linear search and linear probing in hash tables) which access elements in sequence. This adds slightly to the running time of binary search for large arrays on most systems.[5]

二分搜索与其他方案

Sorted arrays with binary search are a very inefficient solution when insertion and deletion operations are interleaved with retrieval, taking O(n) time for each such operation. In addition, sorted arrays can complicate memory use especially when elements are often inserted into the array.Template:Sfn There are other data structures that support much more efficient insertion and deletion. Binary search can be used to perform exact matching and set membership (determining whether a target value is in a collection of values). There are data structures that support faster exact matching and set membership. However, unlike many other searching schemes, binary search can be used for efficient approximate matching, usually performing such matches in O(logn) time regardless of the type or structure of the values themselves.[6] In addition, there are some operations, like finding the smallest and largest element, that can be performed efficiently on a sorted array.Template:Sfn

线性搜索

Linear search is a simple search algorithm that checks every record until it finds the target value. Linear search can be done on a linked list, which allows for faster insertion and deletion than an array. Binary search is faster than linear search for sorted arrays except if the array is short, although the array needs to be sorted beforehand.Template:EfnTemplate:Sfn All sorting algorithms based on comparing elements, such as quicksort and merge sort, require at least O(nlogn) comparisons in the worst case.Template:Sfn Unlike linear search, binary search can be used for efficient approximate matching. There are operations such as finding the smallest and largest element that can be done efficiently on a sorted array but not on an unsorted array.Template:Sfn

二叉树

Binary search trees are searched using an algorithm similar to binary search.

A binary search tree is a binary tree data structure that works based on the principle of binary search. The records of the tree are arranged in sorted order, and each record in the tree can be searched using an algorithm similar to binary search, taking on average logarithmic time. Insertion and deletion also require on average logarithmic time in binary search trees. This can be faster than the linear time insertion and deletion of sorted arrays, and binary trees retain the ability to perform all the operations possible on a sorted array, including range and approximate queries.[6]Template:Sfn

However, binary search is usually more efficient for searching as binary search trees will most likely be imperfectly balanced, resulting in slightly worse performance than binary search. This even applies to balanced binary search trees, binary search trees that balance their own nodes, because they rarely produce the tree with the fewest possible levels. Except for balanced binary search trees, the tree may be severely imbalanced with few internal nodes with two children, resulting in the average and worst-case search time approaching n comparisons.Template:Efn Binary search trees take more space than sorted arrays.Template:Sfn

Binary search trees lend themselves to fast searching in external memory stored in hard disks, as binary search trees can be efficiently structured in filesystems. The B-tree generalizes this method of tree organization. B-trees are frequently used to organize long-term storage such as databases and filesystems.Template:SfnTemplate:Sfn

散列表

For implementing associative arrays, hash tables, a data structure that maps keys to records using a hash function, are generally faster than binary search on a sorted array of records.Template:Sfn Most hash table implementations require only amortized constant time on average.Template:Efn[7] However, hashing is not useful for approximate matches, such as computing the next-smallest, next-largest, and nearest key, as the only information given on a failed search is that the target is not present in any record.[8] Binary search is ideal for such matches, performing them in logarithmic time. Binary search also supports approximate matches. Some operations, like finding the smallest and largest element, can be done efficiently on sorted arrays but not on hash tables.[6]

集合

A related problem to search is set membership. Any algorithm that does lookup, like binary search, can also be used for set membership. There are other algorithms that are more specifically suited for set membership. A bit array is the simplest, useful when the range of keys is limited. It compactly stores a collection of bits, with each bit representing a single key within the range of keys. Bit arrays are very fast, requiring only O(1) time.Template:Sfn The Judy1 type of Judy array handles 64-bit keys efficiently.[9]

For approximate results, Bloom filters, another probabilistic data structure based on hashing, store a set of keys by encoding the keys using a bit array and multiple hash functions. Bloom filters are much more space-efficient than bit arrays in most cases and not much slower: with k hash functions, membership queries require only O(k) time. However, Bloom filters suffer from false positives.Template:EfnTemplate:Efn[10]

其他数据结构

There exist data structures that may improve on binary search in some cases for both searching and other operations available for sorted arrays. For example, searches, approximate matches, and the operations available to sorted arrays can be performed more efficiently than binary search on specialized data structures such as van Emde Boas trees, fusion trees, tries, and bit arrays. These specialized data structures are usually only faster because they take advantage of the properties of keys with a certain attribute (usually keys that are small integers), and thus will be time or space consuming for keys that lack that attribute.[6] As long as the keys can be ordered, these operations can always be done at least efficiently on a sorted array regardless of the keys. Some structures, such as Judy arrays, use a combination of approaches to mitigate this while retaining efficiency and the ability to perform approximate matching.[9]

其他形式

统一二分查找

Template:Main

Uniform binary search stores the difference between the current and the two next possible middle elements instead of specific bounds.

Uniform binary search stores, instead of the lower and upper bounds, the difference in the index of the middle element from the current iteration to the next iteration. A lookup table containing the differences is computed beforehand. For example, if the array to be searched is Template:Math, the middle element (m) would be Template:Math. In this case, the middle element of the left subarray (Template:Math) is Template:Math and the middle element of the right subarray (Template:Math) is Template:Math. Uniform binary search would store the value of Template:Math as both indices differ from Template:Math by this same amount.Template:Sfn To reduce the search space, the algorithm either adds or subtracts this change from the index of the middle element. Uniform binary search may be faster on systems where it is inefficient to calculate the midpoint, such as on decimal computers.Template:Sfn

指数搜索Template:Anchor

Template:Main

Visualization of exponential searching finding the upper bound for the subsequent binary search

Exponential search extends binary search to unbounded lists. It starts by finding the first element with an index that is both a power of two and greater than the target value. Afterwards, it sets that index as the upper bound, and switches to binary search. A search takes log2x+1 iterations before binary search is started and at most log2x iterations of the binary search, where x is the position of the target value. Exponential search works on bounded lists, but becomes an improvement over binary search only if the target value lies near the beginning of the array.Template:Sfn

插值搜索

Template:Main

Visualization of interpolation search using linear interpolation. In this case, no searching is needed because the estimate of the target's location within the array is correct. Other implementations may specify another function for estimating the target's location.

Instead of calculating the midpoint, interpolation search estimates the position of the target value, taking into account the lowest and highest elements in the array as well as length of the array. It works on the basis that the midpoint is not the best guess in many cases. For example, if the target value is close to the highest element in the array, it is likely to be located near the end of the array.Template:Sfn

A common interpolation function is linear interpolation. If A is the array, L,R are the lower and upper bounds respectively, and T is the target, then the target is estimated to be about (TAL)/(ARAL) of the way between L and R. When linear interpolation is used, and the distribution of the array elements is uniform or near uniform, interpolation search makes O(loglogn) comparisons.Template:SfnTemplate:Sfn[11]

In practice, interpolation search is slower than binary search for small arrays, as interpolation search requires extra computation. Its time complexity grows more slowly than binary search, but this only compensates for the extra computation for large arrays.Template:Sfn

分数级联

Template:Main

In fractional cascading, each array has pointers to every second element of another array, so only one binary search has to be performed to search all the arrays.

Fractional cascading is a technique that speeds up binary searches for the same element in multiple sorted arrays. Searching each array separately requires O(klogn) time, where k is the number of arrays. Fractional cascading reduces this to O(k+logn) by storing specific information in each array about each element and its position in the other arrays.[12][13]

Fractional cascading was originally developed to efficiently solve various computational geometry problems. Fractional cascading has been applied elsewhere, such as in data mining and Internet Protocol routing.[12]

推广到图表

Binary search has been generalized to work on certain types of graphs, where the target value is stored in a vertex instead of an array element. Binary search trees are one such generalization—when a vertex (node) in the tree is queried, the algorithm either learns that the vertex is the target, or otherwise which subtree the target would be located in. However, this can be further generalized as follows: given an undirected, positively weighted graph and a target vertex, the algorithm learns upon querying a vertex that it is equal to the target, or it is given an incident edge that is on the shortest path from the queried vertex to the target. The standard binary search algorithm is simply the case where the graph is a path. Similarly, binary search trees are the case where the edges to the left or right subtrees are given when the queried vertex is unequal to the target. For all undirected, positively weighted graphs, there is an algorithm that finds the target vertex in O(logn) queries in the worst case.[14]

嘈杂二分查找

In noisy binary search, there is a certain probability that a comparison is incorrect.

Noisy binary search algorithms solve the case where the algorithm cannot reliably compare elements of the array. For each pair of elements, there is a certain probability that the algorithm makes the wrong comparison. Noisy binary search can find the correct position of the target with a given probability that controls the reliability of the yielded position. Every noisy binary search procedure must make at least (1τ)log2(n)H(p)10H(p) comparisons on average, where H(p)=plog2(p)(1p)log2(1p) is the binary entropy function and τ is the probability that the procedure yields the wrong position.[15][16][17] The noisy binary search problem can be considered as a case of the Rényi-Ulam game,[18] a variant of Twenty Questions where the answers may be wrong.[19]

量子二分查找

Classical computers are bounded to the worst case of exactly log2n+1 iterations when performing binary search. Quantum algorithms for binary search are still bounded to a proportion of log2n queries (representing iterations of the classical procedure), but the constant factor is less than one, providing for a lower time complexity on quantum computers. Any exact quantum binary search procedure—that is, a procedure that always yields the correct result—requires at least 1π(lnn1)0.22log2n queries in the worst case, where ln is the natural logarithm.[20] There is an exact quantum binary search procedure that runs in 4log605n0.433log2n queries in the worst case.[21] In comparison, Grover's algorithm is the optimal quantum algorithm for searching an unordered list of elements, and it requires O(n) queries.[22]

历史

The idea of sorting a list of items to allow for faster searching dates back to antiquity. The earliest known example was the Inakibit-Anu tablet from Babylon dating back to Template:Circa. The tablet contained about 500 sexagesimal numbers and their reciprocals sorted in lexicographical order, which made searching for a specific entry easier. In addition, several lists of names that were sorted by their first letter were discovered on the Aegean Islands. Catholicon, a Latin dictionary finished in 1286 CE, was the first work to describe rules for sorting words into alphabetical order, as opposed to just the first few letters.Template:Sfn

In 1946, John Mauchly made the first mention of binary search as part of the Moore School Lectures, a seminal and foundational college course in computing.Template:Sfn In 1957, William Wesley Peterson published the first method for interpolation search.Template:Sfn[23] Every published binary search algorithm worked only for arrays whose length is one less than a power of twoTemplate:Efn until 1960, when Derrick Henry Lehmer published a binary search algorithm that worked on all arrays.[24] In 1962, Hermann Bottenbruch presented an ALGOL 60 implementation of binary search that placed the comparison for equality at the end, increasing the average number of iterations by one, but reducing to one the number of comparisons per iteration.[3] The uniform binary search was developed by A. K. Chandra of Stanford University in 1971.Template:Sfn In 1986, Bernard Chazelle and Leonidas J. Guibas introduced fractional cascading as a method to solve numerous search problems in computational geometry.[12][25][26]

实现问题

Template:Blockquote

乔恩·本特利在为职业程序员开设的一门课程中布置了二分查找的练习,发现90%的学生在数小时后仍未能给出正确的解答。主要原因是算法实现有误而无法运行,或是在极少数Template:Le下返回错误答案。Template:Sfn1988年发表的一项研究显示,二十本教材中只有五本给出了准确的二分查找代码。[27]此外,本特利自身在1986年出版的《编程珠玑》一书中给出的二分查找实现存在溢出错误,这个错误在二十多年里未被发现。Java编程语言库中的二分查找实现也存在相同的溢出问题,且该问题持续了九年多。[28]

在实际编程中,表示索引的变量通常是固定大小的(整数)。因此在处理非常大的数组时,可能会导致算术溢出。如果使用L+R2计算中点,即使LR的值都在所用数据类型的表示范围内,L+R的值仍可能会超过范围。如果LR都是非负数,可以通过计算L+RL2来避免这种情况。[29]

如果循环的退出条件定义不正确,可能会导致无限循环。当L超过R时,表示搜索失败,必须返回失败的信息。另外,循环应在找到目标元素时退出;若不这么做,那么在循环结束后,必须检查是否成功找到目标元素。本特利发现,大多数在实现二分查找时出错的程序员,都是在定义退出条件时犯了错。[3]Template:Sfn

库支持

许多编程语言的标准库包含二分查找例程:

  • C语言在其标准库中提供了bsearch()函数,通常使用二分查找实现,尽管官方标准中并未强制要求。[30]
  • C++标准库中提供了binary_search()lower_bound()upper_bound()equal_range()函数。Template:Sfn
  • D语言的标准库Phobos在std.range模块中提供了SortedRange类型(由sort()assumeSorted()函数返回),该类型包含contains()equaleRange()lowerBound()trisect()方法,这些方法默认对提供随机访问的范围使用二分查找技术。[31]
  • COBOL提供了SEARCH ALL动词,用于对COBOL有序表执行二分查找。[32]
  • Gosort标准库包包含SearchSearchIntsSearchFloat64sSearchStrings函数,分别实现了通用的二分查找,以及针对整数、浮点数、字符串切片的特定实现。[33]
  • Java在标准java.util包的Template:Javadoc:SETemplate:Javadoc:SE类中提供了一组重载binarySearch()静态方法,用于对Java数组和List(列表)执行二分查找。[34][35]
  • Microsoft.NET Framework 2.0在其集合基类中提供了二分查找算法的静态泛型版本,例如System.ArrayBinarySearch<T>(T[] array, T value)方法。[36]
  • 对于Objective-CCocoa框架Mac OS X 10.6及以上版本中提供了NSArray -indexOfObject:inSortedRange:options:usingComparator:方法;[37]苹果的Template:Le C框架也包含CFArrayBSearchValues()函数。[38]
  • Python提供了模块bisect,在插入元素后仍能保持列表的有序状态,而无需每次插入元素后都对列表排序。[39]
  • Ruby的Array类包含一个带有内置近似匹配的bsearch方法。Template:Sfn
  • Rust的切片原始类型提供了binary_search()binary_search_by()binary_search_by_key()partition_point()方法。[40]

参见

注释和参考文献

注释

Template:Notelist

引用

Template:Reflist

来源

Template:Columns-list


外部链接

Template:算法