en/docs/chapter_searching/binary_search.md
<u>Binary search</u> is an efficient searching algorithm based on the divide-and-conquer strategy. It leverages the orderliness of data to reduce the search range by half in each round until the target element is found or the search interval becomes empty.
!!! question
Given an array `nums` of length $n$ with elements arranged in ascending order and no duplicates, search for and return the index of element `target` in the array. If the array does not contain the element, return $-1$. An example is shown in the figure below.
As shown in the figure below, we first initialize pointers $i = 0$ and $j = n - 1$, pointing to the first and last elements of the array respectively, representing the search interval $[0, n - 1]$. Note that square brackets denote a closed interval, which includes the boundary values themselves.
Next, perform the following two steps in a loop:
nums[m] and target, which results in three cases:
nums[m] < target, it indicates that target is in the interval $[m + 1, j]$, so execute $i = m + 1$.nums[m] > target, it indicates that target is in the interval $[i, m - 1]$, so execute $j = m - 1$.nums[m] = target, it indicates that target has been found, so return index $m$.If the array does not contain the target element, the search interval will eventually shrink to empty. In this case, return $-1$.
=== "<1>"
=== "<2>"
=== "<3>"
=== "<4>"
=== "<5>"
=== "<6>"
=== "<7>"
It's worth noting that since both $i$ and $j$ are of int type, $i + j$ may exceed the range of the int type. To avoid large number overflow, we typically use the formula $m = \lfloor {i + (j - i) / 2} \rfloor$ to calculate the midpoint.
The code is shown below:
[file]{binary_search}-[class]{}-[func]{binary_search}
Time complexity is $O(\log n)$: In the binary loop, the interval is reduced by half each round, so the number of loops is $\log_2 n$.
Space complexity is $O(1)$: Pointers $i$ and $j$ use constant-size space.
In addition to the closed interval mentioned above, another common interval representation is the "left-closed right-open" interval, defined as $[0, n)$, meaning the left boundary includes itself while the right boundary does not. Under this representation, the interval $[i, j)$ is empty when $i = j$.
We can implement a binary search algorithm with the same functionality based on this representation:
[file]{binary_search}-[class]{}-[func]{binary_search_lcro}
As shown in the figure below, under the two interval representations, the initialization, loop condition, and interval narrowing operations of the binary search algorithm are all different.
Since both the left and right boundaries in the "closed interval" representation are defined as closed, the operations to narrow the interval through pointers $i$ and $j$ are also symmetric. This makes it less error-prone, so the "closed interval" approach is generally recommended.
Binary search performs well in both time and space aspects.
However, binary search is not suitable for all situations, mainly for the following reasons: