Binary Search
Problem Statement
You are given an array of distinct integers nums
, sorted in ascending order, and an integer target
.
Implement a function to search for target
within nums
. If it exists, then return its index, otherwise, return -1
.
Your solution must run in O(log n)
time.
Example 1:
Input: nums = [-1,0,2,4,6,8], target = 4
Output: 3
Example 2:
Input: nums = [-1,0,2,4,6,8], target = 3
Output: -1
Constraints:
1 <= nums.length <= 10000
.-10000 < nums[i], target < 10000
You should aim for a solution with O(log n)
time and O(1)
space, where n
is the size of the input array.
Recommended Time and Space Complexity
You should aim for a solution with O(log n)
time and O(1)
space, where n
is the size of the input array.
Hint 1
Can you find an algorithm that is useful when the array is sorted? Maybe other than linear search.
Hint 2
The problem name is the name of the algorithm that we can use. We need to find a target value and if it does not exist in the array return -1
. We have l
and r
as the boundaries of the segment of the array in which we are searching. Try building conditions to eliminate half of the search segment at each step. Maybe the sorted nature of the array can be helpful.
Hint 3
We compare the target value with the mid
of the segment. For example, consider the array [1, 2, 3, 4, 5]
and target = 4
. The mid
value is 3
, thus, on the next iteration we search to the right of mid
. The remaining segment is [4,5]
. Why?
Hint 4
Because the array is sorted, all elements to the left of mid
(including 3
) are guaranteed to be smaller than the target. Therefore, we can safely eliminate that half of the array from consideration, narrowing the search to the right half and repeat this search until we find the target.
Solution
Recursive Binary Search
def binary_search_recursive(l: int, r: int, nums: list[int], target: int) -> int: if l > r: return -1 m = l + (r - l) // 2
if nums[m] == target: return m if nums[m] < target: return binary_search_recursive(m + 1, r, nums, target) return binary_search_recursive(l, m - 1, nums, target)
def search(nums: list[int], target: int) -> int: return binary_search_recursive(0, len(nums) - 1, nums, target)
Time complexity: O(log n)
Space complexity: O(log n)
Iterative Binary Search
def search(nums: list[int], target: int) -> int: l, r = 0, len(nums) - 1
while l <= r: # (l + r) // 2 can lead to overflow m = l + ((r - l) // 2)
if nums[m] > target: r = m - 1 elif nums[m] < target: l = m + 1 else: return m return -1
Time complexity: O(log n)
Space complexity: O(1)
Upper Bound
def search(nums: list[int], target: int) -> int: l, r = 0, len(nums)
while l < r: m = l + ((r - l) // 2) if nums[m] > target: r = m elif nums[m] <= target: l = m + 1 return l - 1 if (l and nums[l - 1] == target) else -1
Time complexity: O(log n)
Space complexity: O(1)
Lower Bound
def search(nums: list[int], target: int) -> int: l, r = 0, len(nums)
while l < r: m = l + ((r - l) // 2) if nums[m] >= target: r = m elif nums[m] < target: l = m + 1 return l if (l < len(nums) and nums[l] == target) else -1
Time complexity: O(log n)
Space complexity: O(1)
Built-In Function
import bisect
def search(nums: list[int], target: int) -> int: index = bisect.bisect_left(nums, target) return index if index < len(nums) and nums[index] == target else -1
Time complexity: O(log n)
Space complexity: O(1)