You are given a sorted array nums of distinct integers and a value target.
Return the index of target if it is present in nums. If it is not, return the index where it would be inserted to keep the array sorted.
Your algorithm must run in O(log n) time.
n itself is a valid answer — if target is larger than every element, it inserts at the end. Starting hi = n - 1 makes that index unreachable. The window [lo, hi) is half-open: hi is a candidate answer, not a candidate array index to read.i with nums[i] >= target. If target exists (values are distinct), that first >= is an exact match, so i is its index. If it does not exist, i is precisely the gap where it belongs. No separate branches needed.nums[mid] >= target, index mid might itself be the answer — the first such index. hi = mid keeps it inside the window; hi = mid - 1 could discard the correct answer and return an index one too far left.lo up (lo = mid + 1) or moves hi down to mid, and since mid < hi always holds inside the loop, the window hi - lo strictly shrinks every step until it hits zero.nums[i] >= target (the lower bound). That robustness is why the nums[mid] < target comparison, not <=, is the version worth memorizing.target (first occurrence) and once for target + 1, then subtract one from that result (last occurrence). If the two bounds are equal, the target is absent.bisect.bisect_left, C++'s std::lower_bound, and Go's sort.SearchInts. Interviewers usually want the loop written by hand once, then are happy to see you know the standard-library name for it.Input: nums = [1,3,5,6], target = 5 Output: 2 `5` is already in the array at index `2`.
Input: nums = [1,3,5,6], target = 2 Output: 1 `2` is not present. It would slot in between `1` and `3` — at index `1`.
Input: nums = [1,3,5,6], target = 7 Output: 4 `7` is larger than everything, so it goes at the end — index `4`.
- 1 <= nums.length <= 10^4 - -10^4 <= nums[i] <= 10^4 - nums contains distinct values sorted in ascending order. - -10^4 <= target <= 10^4
This is the cleanest possible introduction to binary search on the answer's position — the lower bound pattern. Master it here and you will reuse it in dozens of harder problems.
lo and hi, until they meet.[lo, hi) as: every index before lo is decided, every index from hi onward is decided, and the answer lies in between.In plain English: find the first index i where nums[i] >= target. If target exists, that index is its position (values are distinct). If it does not exist, that index is exactly where it should be inserted. If every element is smaller, the answer is nums.length. One rule covers found, not-found, and end-of-array — no special cases.
Worked example — nums = [1,3,5,6], target = 2
index: 0 1 2 3
value: 1 3 5 6
^
first value >= 2 is 3, at index 1
answer: 1 (insert 2 between 1 and 3)
Asking two or three sharp questions before coding shows an interviewer you design before you type.
“Is the array guaranteed to be sorted, and are all values distinct?”
Both — sortedness makes binary search valid at all, and distinctness means a found target has exactly one index, so no leftmost-vs-rightmost ambiguity.
“Can the array be empty?”
Here nums.length >= 1, but the lower-bound loop handles an empty array for free — it returns 0 without any special case.
“What if target is smaller than every element, or larger than every element?”
The answer should be 0 and nums.length respectively — verify your loop returns both without extra branches.
“What should happen with a single-element array?”
Three outcomes from one element: target smaller gives 0, equal gives 0, larger gives 1. A great 10-second sanity check.
“How large can the array get, and is O(log n) required?”
n up to 10^4. A linear scan would pass on size alone, but the statement explicitly demands O(log n) — so binary search is the expected answer, not just the fancy one.
Before I code, let me confirm the array is sorted ascending with distinct values.
Since you require O(log n), I will binary search — but for the first index whose value is at least the target, which handles found and not-found uniformly.
I will sanity-check the two boundary cases: a target below the minimum should return 0, and above the maximum should return the length.
Both cases reduce to one query: the first index where nums[i] >= target (the lower bound). If target is present, that index points at it. If absent, it points at the gap where it belongs. Unifying the two cases is the entire trick.
Because nums is sorted, the predicate nums[i] >= target is false, false, ..., false, true, true, ..., true — a wall of NOs followed by a wall of YESes. Binary search finds the boundary between them.
nums = [1, 3, 5, 6], target = 5
>= 5 ? N N Y Y
^ first YES → answer 2Start with lo = 0, hi = n — note hi starts past the last index, because n itself is a legal answer. Each step: if nums[mid] < target, the boundary is to the right (lo = mid + 1); otherwise mid might be the boundary, so keep it (hi = mid). When lo == hi, both point at the answer.
| Linear scan | Binary search | |
|---|---|---|
| Time | O(n) | O(log n) |
| Space | O(1) | O(1) |
| Comparisons for n = 10^4 | up to 10,000 | about 14 |
Full code for both approaches is in the Approaches selector below.
Key takeaway
Search Insert Position is the lower bound pattern: binary search on a half-open window [lo, hi) for the first index where a sorted predicate flips from false to true. The same loop later solves first/last occurrence, floor/ceiling queries, and every binary-search-on-answer problem.
lo = 0, hi = n
while lo < hi:
mid = (lo + hi) / 2
if nums[mid] < target: lo = mid + 1
else: hi = mid
return lo