Facebook Pixel

35. Search Insert Position

Problem Description

You are given a sorted array of distinct integers and a target value. Your task is to find where the target value should be placed in the array to maintain the sorted order.

If the target value already exists in the array, return its index. If the target value doesn't exist, return the index where it would be inserted to keep the array sorted.

For example:

  • If nums = [1, 3, 5, 6] and target = 5, the function should return 2 because 5 exists at index 2
  • If nums = [1, 3, 5, 6] and target = 2, the function should return 1 because 2 would be inserted at index 1 to maintain sorted order
  • If nums = [1, 3, 5, 6] and target = 7, the function should return 4 because 7 would be inserted at the end

The algorithm must have O(log n) runtime complexity, which means you need to use binary search rather than a linear scan through the array.

The solution uses binary search to efficiently find the insertion position. It maintains two pointers l (left) and r (right) to define the search range. In each iteration, it calculates the middle index and compares nums[mid] with the target. If nums[mid] >= target, it means the target should be inserted at or before the middle position, so the search continues in the left half. Otherwise, the search continues in the right half. When the loop ends, l points to the correct insertion position.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since we need O(log n) complexity and the array is sorted, binary search is the natural choice. The key insight is that we're not just searching for an exact match - we're looking for the correct insertion position.

Think of binary search as repeatedly dividing the search space in half. At each step, we compare the middle element with our target. If nums[mid] >= target, we know the target belongs somewhere in the left half (including possibly at the current middle position). If nums[mid] < target, the target must go somewhere in the right half.

The clever part is recognizing that we want to find the leftmost position where we could insert the target. This is why when nums[mid] >= target, we set r = mid (not mid - 1). We're saying "the answer could be at mid or somewhere to its left." When nums[mid] < target, we know for certain the answer is to the right of mid, so we set l = mid + 1.

By the time our search window shrinks to nothing (l >= r), l will be pointing exactly where the target should go:

  • If the target exists, l points to its position
  • If the target doesn't exist, l points to where it would maintain sorted order

This approach elegantly handles both cases (element exists or doesn't exist) with the same logic, avoiding the need for special case handling.

Learn more about Binary Search patterns.

Solution Approach

The implementation uses binary search with a two-pointer approach. Here's how the algorithm works step by step:

Initial Setup:

  • Initialize l = 0 (left boundary) and r = len(nums) (right boundary)
  • Note that r is set to the array length, not len(nums) - 1, allowing us to handle insertion at the end of the array

Binary Search Loop: The loop continues while l < r:

  1. Calculate the middle index: mid = (l + r) >> 1

    • The >> 1 operation is a bitwise right shift, equivalent to integer division by 2
    • This efficiently finds the midpoint of our current search range
  2. Compare nums[mid] with target:

    • If nums[mid] >= target: Set r = mid
      • This means the target should be at position mid or somewhere to its left
      • We keep mid in our search range because it could be the answer
    • If nums[mid] < target: Set l = mid + 1
      • The target must be inserted after position mid
      • We can safely exclude mid from our search range

Loop Termination: When l >= r, the search window has converged to a single position. At this point, l represents:

  • The index of the target if it exists in the array
  • The index where the target should be inserted if it doesn't exist

Return Value: The function returns l, which is the correct insertion position.

Example Walkthrough: For nums = [1, 3, 5, 6] and target = 2:

  • Initial: l = 0, r = 4
  • Iteration 1: mid = 2, nums[2] = 5 >= 2, so r = 2
  • Iteration 2: mid = 1, nums[1] = 3 >= 2, so r = 1
  • Iteration 3: mid = 0, nums[0] = 1 < 2, so l = 1
  • Loop ends as l == r == 1
  • Returns 1, the correct insertion position

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through finding the insertion position for target = 4 in the array nums = [1, 3, 5, 7, 9].

Initial State:

  • Array: [1, 3, 5, 7, 9]
  • Target: 4 (doesn't exist, should go between 3 and 5)
  • l = 0, r = 5 (array length)

Iteration 1:

  • Calculate mid = (0 + 5) >> 1 = 2
  • Check nums[2] = 5
  • Since 5 >= 4, the target belongs at position 2 or to its left
  • Update: r = 2
  • Search range: [1, 3, 5] (indices 0-1, with 2 as potential answer)

Iteration 2:

  • Calculate mid = (0 + 2) >> 1 = 1
  • Check nums[1] = 3
  • Since 3 < 4, the target must go after position 1
  • Update: l = 2
  • Search range narrows to index 2

Termination:

  • Now l = 2 and r = 2, so l >= r
  • Loop ends
  • Return l = 2

Verification: Inserting 4 at index 2 would give us [1, 3, 4, 5, 7, 9], which maintains sorted order. The algorithm correctly identified that 4 should be placed where 5 currently is, shifting 5 and subsequent elements to the right.

Key Insight: Notice how the algorithm naturally converges to the correct position. When we find an element greater than or equal to our target (5 in this case), we keep it as a candidate position. When we find an element less than our target (3 in this case), we know the answer must be after it. The two pointers eventually meet at the exact insertion point.

Solution Implementation

1class Solution:
2    def searchInsert(self, nums: List[int], target: int) -> int:
3        """
4        Find the index where target should be inserted in a sorted array.
5        If target exists, return its index. Otherwise, return the insertion position.
6      
7        Args:
8            nums: A sorted array of integers in ascending order
9            target: The target value to search for or insert
10          
11        Returns:
12            The index where target is found or should be inserted
13        """
14        # Initialize binary search boundaries
15        # left points to the start, right points one past the end (exclusive)
16        left, right = 0, len(nums)
17      
18        # Binary search to find the leftmost position where nums[i] >= target
19        while left < right:
20            # Calculate middle index using bit shift (equivalent to // 2)
21            mid = (left + right) >> 1
22          
23            # If middle element is greater than or equal to target,
24            # the answer is in the left half (including mid)
25            if nums[mid] >= target:
26                right = mid
27            else:
28                # Otherwise, the answer is in the right half (excluding mid)
29                left = mid + 1
30      
31        # left now points to the first position where nums[i] >= target
32        # or to len(nums) if all elements are smaller than target
33        return left
34
1class Solution {
2    public int searchInsert(int[] nums, int target) {
3        // Initialize binary search boundaries
4        // left: inclusive start index
5        // right: exclusive end index (initially set to array length)
6        int left = 0;
7        int right = nums.length;
8      
9        // Binary search loop continues while search space is not empty
10        while (left < right) {
11            // Calculate middle index using unsigned right shift to avoid overflow
12            // Equivalent to (left + right) / 2 but prevents integer overflow
13            int mid = (left + right) >>> 1;
14          
15            // Check if middle element is greater than or equal to target
16            if (nums[mid] >= target) {
17                // Target is in the left half (including mid)
18                // Move right boundary to mid (exclusive)
19                right = mid;
20            } else {
21                // Target is in the right half (excluding mid)
22                // Move left boundary to mid + 1 (inclusive)
23                left = mid + 1;
24            }
25        }
26      
27        // Return the insertion position
28        // At this point, left == right, representing the smallest index
29        // where nums[index] >= target, or array length if target > all elements
30        return left;
31    }
32}
33
1class Solution {
2public:
3    int searchInsert(vector<int>& nums, int target) {
4        // Initialize binary search boundaries
5        // left: inclusive start index
6        // right: exclusive end index (nums.size())
7        int left = 0;
8        int right = nums.size();
9      
10        // Binary search to find the leftmost position where target should be inserted
11        while (left < right) {
12            // Calculate middle index using bit shift (equivalent to dividing by 2)
13            int mid = (left + right) >> 1;
14          
15            // If middle element is greater than or equal to target,
16            // the insertion position is at mid or to the left of mid
17            if (nums[mid] >= target) {
18                right = mid;  // Narrow search to left half (excluding mid)
19            } else {
20                // If middle element is less than target,
21                // the insertion position is to the right of mid
22                left = mid + 1;  // Narrow search to right half (excluding mid)
23            }
24        }
25      
26        // When left == right, we've found the insertion position
27        // This is the smallest index where nums[index] >= target
28        // or nums.size() if all elements are smaller than target
29        return left;
30    }
31};
32
1/**
2 * Binary search to find the index where target should be inserted in a sorted array
3 * @param nums - Sorted array of numbers in ascending order
4 * @param target - Target value to search for or insert
5 * @returns The index where target is found or should be inserted to maintain sorted order
6 */
7function searchInsert(nums: number[], target: number): number {
8    // Initialize binary search boundaries
9    // left starts at 0, right starts at array length (exclusive)
10    let left: number = 0;
11    let right: number = nums.length;
12  
13    // Continue binary search while search space is valid
14    while (left < right) {
15        // Calculate middle index using bit shift for integer division
16        // Equivalent to Math.floor((left + right) / 2)
17        const middle: number = (left + right) >> 1;
18      
19        // If middle element is greater than or equal to target,
20        // search in the left half (including middle)
21        if (nums[middle] >= target) {
22            right = middle;
23        } else {
24            // If middle element is less than target,
25            // search in the right half (excluding middle)
26            left = middle + 1;
27        }
28    }
29  
30    // Return the insertion position
31    // At this point, left === right, representing the correct position
32    return left;
33}
34

Time and Space Complexity

The time complexity is O(log n), where n is the length of the array nums. This is because the algorithm uses binary search, which divides the search space in half with each iteration. The while loop continues until l < r is false, and in each iteration, we either set r = mid or l = mid + 1, effectively halving the search range. Therefore, the maximum number of iterations is log₂(n).

The space complexity is O(1) as the algorithm only uses a constant amount of extra space. The variables l, r, and mid are the only additional space used, regardless of the input size. No recursive calls are made, and no additional data structures are created that depend on the input size.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Right Boundary Initialization

One of the most common mistakes is initializing right = len(nums) - 1 instead of right = len(nums). This seems intuitive since array indices go from 0 to len(nums) - 1, but it causes problems when the target should be inserted at the end of the array.

Incorrect Implementation:

def searchInsert(self, nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1  # Wrong!
  
    while left < right:
        mid = (left + right) >> 1
        if nums[mid] >= target:
            right = mid
        else:
            left = mid + 1
  
    return left

Problem: When target = 7 and nums = [1, 3, 5, 6], this returns index 3 instead of 4. The algorithm never considers position 4 as a valid insertion point.

Solution: Always use right = len(nums) to include the position after the last element as a valid insertion point.

2. Infinite Loop with Wrong Update Logic

Another pitfall occurs when using left <= right as the loop condition with incorrect pointer updates.

Incorrect Implementation:

def searchInsert(self, nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1
  
    while left <= right:  # Different condition
        mid = (left + right) >> 1
        if nums[mid] >= target:
            right = mid  # This can cause infinite loop!
        else:
            left = mid + 1
  
    return left

Problem: When left == right, setting right = mid doesn't change the search range, causing an infinite loop.

Solution: Either use left < right with right = mid, or use left <= right with right = mid - 1.

3. Integer Overflow in Mid Calculation

In languages with fixed integer sizes (like Java or C++), calculating mid = (left + right) / 2 can cause integer overflow when left + right exceeds the maximum integer value.

Safe Calculation Methods:

# Method 1: Use subtraction to avoid overflow
mid = left + (right - left) // 2

# Method 2: Bitwise operation (works in Python)
mid = (left + right) >> 1

# Method 3: Python's integer division (no overflow in Python)
mid = (left + right) // 2

4. Handling Edge Cases Incorrectly

Failing to handle empty arrays or single-element arrays properly.

Robust Implementation with Edge Case Handling:

def searchInsert(self, nums: List[int], target: int) -> int:
    # Handle empty array
    if not nums:
        return 0
  
    # Standard binary search
    left, right = 0, len(nums)
  
    while left < right:
        mid = (left + right) >> 1
        if nums[mid] >= target:
            right = mid
        else:
            left = mid + 1
  
    return left

Note: The original solution actually handles empty arrays correctly since when nums = [], left = 0 and right = 0, so the loop doesn't execute and returns 0, which is correct.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement priority queue?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More