35. Search Insert Position
Difficulty: Easy
Problem Statement
Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You mustΒ write an algorithm withΒ O(log n) runtime complexity.
Example 1:
Input: nums = [1,3,5,6], target = 5
Output: 2
Example 2:
Input: nums = [1,3,5,6], target = 2
Output: 1
Example 3:
Input: nums = [1,3,5,6], target = 7
Output: 4
Constraints:
1 <= nums.length <= 104-104 <= nums[i] <= 104numscontains distinct values sorted in ascending order.-104 <= target <= 104
Intuition
When I saw that an $O(\log N)$ solution is explicitly required, I immediately thought back to #14, where binary search was used to progressively discard parts of the search space that could not contain the answer.
Since the array is already sorted, we can use binary search instead of scanning every element. At each step, we compare the target with the middle element. If the target is larger, we know it cannot appear in the left half, so we discard it. Otherwise, we discard the right half. If the target is found, we return its index immediately. If the search space becomes empty, the left pointer indicates the position where the target should be inserted while preserving the sorted order.
Algorithm
graph TD
START(("<b>searchInsert</b>"))
Inputs[/"
<b>nums</b>: List[int];
<b>target</b>: int"/]
markers[compute <b>left</b>, and <b>right</b>]
while{ left <= right? }
mid["compute <b>mid</b>"]
found{"nums[mid] == target?"}
greater{" target > nums[mid]"}
stripL[left = mid + 1]
stripR[right = mid - 1]
continue[continue]
retMid([return <b>mid</b>])
START --> Inputs --> markers --> while
while -- yes --> mid --> found
found -- yes --> retMid
found -- no --> greater
greater -- yes --> stripL
greater -- no --> stripR
stripL & stripR --> continue --> while
while -- no --> retLeft[return <b>left</b>]
Testing
The test approach is classic expected vs yielded values for the returning int. We cover key case examples where the number is and isn' t found in the original array, as well as the case where the input array is empty.
if __name__ == "__main__":
sol = Solution()
test_cases = [
([1, 2, 3, 4, 5], 5, 4),
([1, 3, 5, 6], 5, 2),
([1, 3, 5, 6], 2, 1),
([1, 3, 5, 6], 7, 4),
([1, 2, 3, 5, 6], 4, 3),
([], 1, 0)
]
for i, (nums, target, expected_output) in enumerate(test_cases, start=1):
res = sol.searchInsert(nums, target)
if res == expected_output:
print(f"Test {i} PASSED")
else:
print(f"Test {i} FAILED:")
print(f" Expected output: {expected_output}, got {res}")
Implementation
Included guardrail for edge case where the input Python list is empty, returning zero immediately. Utmost right position computed as the lenght of the List minus 1.
from typing import List
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
if not nums:
return 0
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if target == nums[mid]:
return mid
elif target > nums[mid]:
left = mid + 1
else:
right = mid - 1
return left
Local testing and LeetCode tests were passed.
Complexity Analysis
-
Time Complexity: $O(\log n)$. Each iteration discards half of the remaining search space, so the number of iterations grows logarithmically with the size of the input.
-
Space Complexity: $O(1)$. The algorithm uses a fixed number of variables (
left,right, andmid) regardless of the input size.
Takeaways
-
Binary search is about maintaining an invariant. At every step, the target (or its insertion position) must lie within the current search interval.
-
Think in terms of search spaces, not arrays. We never modify or slice the array; we only move the
leftandrightboundaries. -
The insertion position emerges naturally. When the loop finishes,
leftpoints to the first position where the target could be inserted while preserving sorted order. This eliminates the need for additional checks after the search.