Binary Search
Difficulty: Easy Source: NeetCode
Problem
Given an array of integers
numswhich is sorted in ascending order, and an integertarget, write a function to searchtargetinnums. Iftargetexists, return its index. Otherwise, return-1.You must write an algorithm with
O(log n)runtime complexity.Example 1: Input:
nums = [-1, 0, 3, 5, 9, 12],target = 9Output:4Example 2: Input:
nums = [-1, 0, 3, 5, 9, 12],target = 2Output:-1Constraints:
1 <= nums.length <= 10^4-10^4 < nums[i], target < 10^4- All integers in
numsare uniquenumsis sorted in ascending order
Prerequisites
Before attempting this problem, you should be comfortable with:
- Sorted Arrays — understanding that sorted order lets you eliminate half the search space at each step
- Index Arithmetic — computing midpoints and adjusting bounds without going out of range
1. Brute Force
Intuition
Scan every element from left to right and return the index the moment you find the target. Simple and correct, but completely ignores the sorted property — you’re doing O(n) work when O(log n) is achievable.
Algorithm
- For each index
ifrom0tolen(nums) - 1:- If
nums[i] == target, returni.
- If
- Return
-1if the loop finishes without a match.
Solution
def search_linear(nums, target):
for i in range(len(nums)):
if nums[i] == target:
return i
return -1
print(search_linear([-1, 0, 3, 5, 9, 12], 9)) # 4
print(search_linear([-1, 0, 3, 5, 9, 12], 2)) # -1
print(search_linear([5], 5)) # 0
Complexity
- Time:
O(n) - Space:
O(1)
2. Binary Search
Intuition
Because the array is sorted, you can compare the middle element with the target and immediately discard half the array. If nums[mid] == target you’re done. If nums[mid] < target the answer must be to the right, so move lo up. If nums[mid] > target it must be to the left, so move hi down. Each iteration halves the search space, giving O(log n).
Algorithm
- Set
lo = 0andhi = len(nums) - 1. - While
lo <= hi:- Compute
mid = lo + (hi - lo) // 2. - If
nums[mid] == target, returnmid. - If
nums[mid] < target, setlo = mid + 1. - Else set
hi = mid - 1.
- Compute
- Return
-1.
flowchart LR
S(["nums=[-1,0,3,5,9,12] target=9\nlo=0 hi=5"])
S --> m0["mid=2 nums[2]=3 < 9 → lo=3"]
m0 --> m1["mid=4 nums[4]=9 = 9 → return 4"]
Solution
def search(nums, target):
lo, hi = 0, len(nums) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
print(search([-1, 0, 3, 5, 9, 12], 9)) # 4
print(search([-1, 0, 3, 5, 9, 12], 2)) # -1
print(search([5], 5)) # 0
Complexity
- Time:
O(log n) - Space:
O(1)
Common Pitfalls
Using mid = (lo + hi) // 2 in languages with fixed-width integers. In Python integers don’t overflow, but in C/Java this can wrap around for large values. Prefer lo + (hi - lo) // 2 as a habit — it’s safe everywhere.
Loop condition lo < hi vs lo <= hi. With lo < hi you exit one iteration early and may never check the element at the final lo position. Use lo <= hi so every candidate gets examined.
Moving lo = mid or hi = mid instead of mid ± 1. This can cause an infinite loop when lo and hi are adjacent — mid would equal lo and the bounds would never change.