Facebook Pixel

33. Search in Rotated Sorted Array

Problem Description

You are given an integer array nums that was originally sorted in ascending order with all distinct values. However, this array may have been left rotated at some unknown pivot index k (where 1 <= k < nums.length).

A left rotation at index k means the array is rearranged from its original order [nums[0], nums[1], ..., nums[n-1]] to [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]].

For example:

  • Original sorted array: [0,1,2,4,5,6,7]
  • After left rotation by 3 indices: [4,5,6,7,0,1,2]

Your task is to search for a given target value in this possibly rotated array and return its index. If the target is not found in the array, return -1.

Key Requirements:

  • The algorithm must have O(log n) runtime complexity
  • The array contains distinct values (no duplicates)
  • The array may or may not be rotated

The challenge is to efficiently search in this rotated sorted array without first finding the rotation point or restoring the original order. Since the required time complexity is O(log n), a linear search is not acceptable - you need to use a modified binary search approach that can handle the rotation.

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

Intuition

When we look at a rotated sorted array, we notice an important property: if we split the array at any point, at least one half will always be properly sorted. This is the key insight that allows us to use binary search even though the entire array isn't sorted.

Let's understand why this works. Consider the array [4,5,6,7,0,1,2]:

  • If we pick the middle element (index 3, value 7), we can check:
    • Left half: [4,5,6,7] - this is sorted (4 ≤ 5 ≤ 6 ≤ 7)
    • Right half: [7,0,1,2] - this is not sorted (7 > 0)

The magic happens because we can easily identify which half is sorted by comparing the first and last elements of that half. If nums[left] <= nums[mid], then the left half is sorted. Otherwise, the right half must be sorted.

Once we know which half is sorted, we can make an intelligent decision about where to search next:

  • If the sorted half contains our target (we can check this easily since it's sorted), we search there
  • Otherwise, the target must be in the unsorted half (if it exists at all)

This approach works because:

  1. We can always identify at least one sorted portion
  2. In a sorted portion, we can definitively determine if the target lies within its range by checking the boundaries
  3. This allows us to eliminate half of the search space in each iteration, maintaining O(log n) complexity

The beauty of this solution is that we don't need to find the rotation point first. We simply adapt our binary search to handle both the sorted and rotated portions dynamically as we encounter them.

Learn more about Binary Search patterns.

Solution Approach

We implement a modified binary search that handles the rotation by identifying which half of the current search range is sorted.

Algorithm Steps:

  1. Initialize pointers: Set left = 0 and right = n - 1 to define our search boundaries.

  2. Binary search loop: While left < right:

    • Calculate the middle index: mid = (left + right) >> 1 (using bit shift for division by 2)
    • Determine which half is sorted by checking if nums[0] <= nums[mid]
  3. Case 1: Left half is sorted (nums[0] <= nums[mid]):

    • The range [0...mid] forms a sorted array
    • Check if target is within this sorted range: nums[0] <= target <= nums[mid]
    • If yes, narrow search to left half: right = mid
    • If no, target must be in right half: left = mid + 1
  4. Case 2: Right half is sorted (when left half is not sorted):

    • The range [mid+1...n-1] must be sorted
    • Check if target is within this sorted range: nums[mid] < target <= nums[n-1]
    • If yes, narrow search to right half: left = mid + 1
    • If no, target must be in left half: right = mid
  5. Final check: After the loop terminates (when left >= right):

    • Check if nums[left] == target
    • Return left if match found, otherwise return -1

Key Implementation Details:

  • We use right = mid instead of right = mid - 1 in some cases because our target might be at mid position
  • The condition nums[0] <= nums[mid] effectively tells us if the rotation point is in the right half
  • We compare with nums[0] and nums[n-1] to determine the boundaries of sorted portions
  • The bit shift operation >> 1 is used for efficient division by 2

Time Complexity: O(log n) - we eliminate half of the search space in each iteration Space Complexity: O(1) - only using a constant amount of extra space for pointers

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's trace through the algorithm with array [4,5,6,7,0,1,2] searching for target = 0.

Initial State:

  • Array: [4,5,6,7,0,1,2] (indices 0-6)
  • left = 0, right = 6, target = 0

Iteration 1:

  • Calculate mid = (0 + 6) >> 1 = 3
  • nums[mid] = 7
  • Check if left half is sorted: nums[0] = 4 <= nums[3] = 7 ✓ (left half [4,5,6,7] is sorted)
  • Is target in sorted left half? Check if 4 <= 0 <= 7
    • No, because 0 < 4
  • Target must be in right half, so set left = mid + 1 = 4

Iteration 2:

  • Current search range: indices 4-6 → [0,1,2]
  • left = 4, right = 6
  • Calculate mid = (4 + 6) >> 1 = 5
  • nums[mid] = 1
  • Check if left half is sorted: nums[0] = 4 <= nums[5] = 1? No (comparing with original nums[0])
  • Since left half is not sorted, right half must be sorted
  • Check if target is in sorted right half: nums[5] = 1 < 0 <= nums[6] = 2?
    • No, because 0 < 1
  • Target must be in left half, so set right = mid = 5

Iteration 3:

  • Current search range: indices 4-5 → [0,1]
  • left = 4, right = 5
  • Calculate mid = (4 + 5) >> 1 = 4
  • nums[mid] = 0
  • Check if left half is sorted: nums[0] = 4 <= nums[4] = 0? No
  • Since left half is not sorted, right half must be sorted
  • Check if target is in sorted right half: nums[4] = 0 < 0 <= nums[6] = 2?
    • No, because 0 = 0 (not strictly greater)
  • Target must be in left half, so set right = mid = 4

Iteration 4:

  • left = 4, right = 4
  • Loop condition left < right is false, exit loop

Final Check:

  • nums[left] = nums[4] = 0
  • Does nums[4] == target? Yes, 0 == 0
  • Return 4

The algorithm successfully finds the target at index 4 in just 3 iterations, demonstrating the O(log n) efficiency.

Solution Implementation

1class Solution:
2    def search(self, nums: List[int], target: int) -> int:
3        """
4        Search for a target value in a rotated sorted array.
5        Returns the index of the target if found, otherwise returns -1.
6      
7        Args:
8            nums: A rotated sorted array with unique elements
9            target: The value to search for
10          
11        Returns:
12            The index of target if found, -1 otherwise
13        """
14        n = len(nums)
15        left, right = 0, n - 1
16      
17        # Binary search to find the target
18        while left < right:
19            # Calculate middle index using bit shift (equivalent to // 2)
20            mid = (left + right) >> 1
21          
22            # Check if the left half is sorted (pivot is in right half)
23            if nums[0] <= nums[mid]:
24                # Target is in the sorted left half
25                if nums[0] <= target <= nums[mid]:
26                    right = mid  # Search in left half including mid
27                else:
28                    left = mid + 1  # Search in right half excluding mid
29            else:
30                # Left half contains the pivot, right half is sorted
31                # Target is in the sorted right half
32                if nums[mid] < target <= nums[n - 1]:
33                    left = mid + 1  # Search in right half excluding mid
34                else:
35                    right = mid  # Search in left half including mid
36      
37        # Check if the element at final position is the target
38        return left if nums[left] == target else -1
39
1class Solution {
2    /**
3     * Search for a target value in a rotated sorted array.
4     * The array was originally sorted in ascending order, then rotated at some pivot.
5     * 
6     * @param nums   the rotated sorted array
7     * @param target the target value to search for
8     * @return       the index of target if found, otherwise -1
9     */
10    public int search(int[] nums, int target) {
11        int arrayLength = nums.length;
12        int left = 0;
13        int right = arrayLength - 1;
14      
15        // Binary search with rotation handling
16        while (left < right) {
17            int mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
18          
19            // Check if the left half is sorted
20            if (nums[0] <= nums[mid]) {
21                // Target is in the sorted left half
22                if (nums[0] <= target && target <= nums[mid]) {
23                    right = mid;  // Search in left half
24                } else {
25                    left = mid + 1;  // Search in right half
26                }
27            } else {
28                // The right half must be sorted
29                // Target is in the sorted right half
30                if (nums[mid] < target && target <= nums[arrayLength - 1]) {
31                    left = mid + 1;  // Search in right half
32                } else {
33                    right = mid;  // Search in left half
34                }
35            }
36        }
37      
38        // Check if the element at the final position is the target
39        return nums[left] == target ? left : -1;
40    }
41}
42
1class Solution {
2public:
3    int search(vector<int>& nums, int target) {
4        int n = nums.size();
5        int left = 0;
6        int right = n - 1;
7      
8        // Binary search in rotated sorted array
9        while (left < right) {
10            // Calculate middle index using bit shift (equivalent to division by 2)
11            int mid = (left + right) >> 1;
12          
13            // Check if the left half is sorted
14            if (nums[0] <= nums[mid]) {
15                // Left half is sorted
16                // Check if target lies in the sorted left half
17                if (nums[0] <= target && target <= nums[mid]) {
18                    // Target is in left half, search left
19                    right = mid;
20                } else {
21                    // Target is in right half, search right
22                    left = mid + 1;
23                }
24            } else {
25                // Right half is sorted
26                // Check if target lies in the sorted right half
27                if (nums[mid] < target && target <= nums[n - 1]) {
28                    // Target is in right half, search right
29                    left = mid + 1;
30                } else {
31                    // Target is in left half, search left
32                    right = mid;
33                }
34            }
35        }
36      
37        // Check if the element at final position is the target
38        return nums[left] == target ? left : -1;
39    }
40};
41
1/**
2 * Searches for a target value in a rotated sorted array using binary search.
3 * The array was originally sorted in ascending order, then rotated at some pivot.
4 * 
5 * @param nums - The rotated sorted array to search in
6 * @param target - The target value to find
7 * @returns The index of the target if found, otherwise -1
8 */
9function search(nums: number[], target: number): number {
10    const arrayLength: number = nums.length;
11    let leftPointer: number = 0;
12    let rightPointer: number = arrayLength - 1;
13  
14    // Binary search with rotation handling
15    while (leftPointer < rightPointer) {
16        // Calculate middle index using bit shift for efficiency
17        const middleIndex: number = (leftPointer + rightPointer) >> 1;
18      
19        // Check if the left half is sorted (no rotation point in left half)
20        if (nums[0] <= nums[middleIndex]) {
21            // Target is within the sorted left half
22            if (nums[0] <= target && target <= nums[middleIndex]) {
23                // Search in the left half
24                rightPointer = middleIndex;
25            } else {
26                // Target must be in the right half
27                leftPointer = middleIndex + 1;
28            }
29        } else {
30            // The rotation point is in the left half, so right half is sorted
31            // Target is within the sorted right half
32            if (nums[middleIndex] < target && target <= nums[arrayLength - 1]) {
33                // Search in the right half
34                leftPointer = middleIndex + 1;
35            } else {
36                // Target must be in the left half
37                rightPointer = middleIndex;
38            }
39        }
40    }
41  
42    // Check if the element at the final position is the target
43    return nums[leftPointer] === target ? leftPointer : -1;
44}
45

Time and Space Complexity

Time Complexity: O(log n), where n is the length of the array nums. The algorithm uses binary search to find the target element in a rotated sorted array. In each iteration of the while loop, the search space is reduced by half (either updating left = mid + 1 or right = mid), which results in at most log₂(n) iterations.

Space Complexity: O(1). The algorithm only uses a constant amount of extra space for the variables n, left, right, and mid, regardless of the input size. No additional data structures or recursive calls are used that would scale with the input size.

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

Common Pitfalls

1. Incorrect Boundary Comparison When Determining the Sorted Half

The Pitfall: A common mistake is using strict inequality (nums[0] < nums[mid]) instead of nums[0] <= nums[mid] when determining which half is sorted. This causes incorrect behavior when nums[0] == nums[mid], which can happen in small arrays or specific rotation scenarios.

Example of the Issue:

# Incorrect implementation
if nums[0] < nums[mid]:  # Wrong! Missing the equal case
    # Left half logic

Consider the array [3, 1] searching for target 1:

  • With strict inequality: 3 < 3 is false, incorrectly assuming right half is sorted
  • With correct comparison: 3 <= 3 is true, correctly identifying left half pattern

Solution: Always use <= to properly handle all cases:

if nums[0] <= nums[mid]:  # Correct
    # Left half is sorted or entire array from 0 to mid is sorted

2. Off-by-One Errors in Range Checking

The Pitfall: When checking if the target falls within a sorted range, developers often make boundary errors by excluding valid endpoints or using incorrect comparison operators.

Example of the Issue:

# Incorrect: Missing boundary values
if nums[0] < target < nums[mid]:  # Wrong! Excludes valid endpoints
    right = mid

# Another mistake: Incorrect boundary for right half
if nums[mid] <= target <= nums[n - 1]:  # Wrong! Would include mid twice
    left = mid + 1

Solution: Use inclusive comparisons for the sorted range boundaries:

# For left sorted half:
if nums[0] <= target <= nums[mid]:
    right = mid  # Include mid since target could equal nums[mid]

# For right sorted half:
if nums[mid] < target <= nums[n - 1]:  # Exclude mid since we already checked it
    left = mid + 1

3. Inconsistent Mid-Point Updates

The Pitfall: Mixing right = mid - 1 and right = mid patterns inconsistently, or using left = mid without adding 1, leading to infinite loops.

Example of the Issue:

# This can cause infinite loop
while left < right:
    mid = (left + right) >> 1
    # ... some conditions ...
    left = mid  # Dangerous! Can cause infinite loop when left = right - 1

Solution: Be consistent with the update pattern:

  • When moving left pointer: always use left = mid + 1
  • When moving right pointer: use right = mid (since we might need to include mid in search)
  • Ensure the loop condition matches the update pattern (while left < right with this approach)

4. Forgetting to Handle Edge Cases

The Pitfall: Not properly handling arrays with only 1 or 2 elements, or not checking if the final position contains the target.

Example of the Issue:

# Forgetting final check
while left < right:
    # ... binary search logic ...
  
return -1  # Wrong! Didn't check if nums[left] == target

Solution: Always validate the final position after the loop:

return left if nums[left] == target else -1

Also ensure the algorithm works for minimal cases:

  • Single element: [1] searching for 1 or 0
  • Two elements: [2, 1] with various targets
  • Non-rotated array: [1, 2, 3, 4, 5]
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

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

Load More