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.
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)
- Left half:
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)
We can frame this as a boundary-finding problem: we're looking for the first index where "we've found the target or passed where it should be." The feasible function considers both the rotation and the target location.
This approach works because:
- We can always identify at least one sorted portion
- In a sorted portion, we can definitively determine if the target lies within its range by checking the boundaries
- 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 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 using the binary search template.
5
6 Args:
7 nums: A rotated sorted array with unique elements
8 target: The value to search for
9
10 Returns:
11 The index of target if found, -1 otherwise
12 """
13 n = len(nums)
14 left, right = 0, n - 1
15 first_true_index = -1
16
17 def feasible(mid: int) -> bool:
18 """
19 Returns True if target is at mid or should be to its left.
20 """
21 if nums[0] <= nums[mid]:
22 # Left half is sorted
23 return nums[0] <= target <= nums[mid]
24 else:
25 # Right half is sorted
26 # Feasible if target is NOT in the sorted right half
27 return not (nums[mid] < target <= nums[n - 1])
28
29 while left <= right:
30 mid = (left + right) // 2
31 if feasible(mid):
32 first_true_index = mid
33 right = mid - 1
34 else:
35 left = mid + 1
36
37 # Check if the element at first_true_index is the target
38 if first_true_index == -1:
39 return -1
40 return first_true_index if nums[first_true_index] == target else -1
411class Solution {
2 /**
3 * Search for a target value in a rotated sorted array using the binary search template.
4 *
5 * @param nums the rotated sorted array
6 * @param target the target value to search for
7 * @return the index of target if found, otherwise -1
8 */
9 public int search(int[] nums, int target) {
10 int n = nums.length;
11 int left = 0;
12 int right = n - 1;
13 int firstTrueIndex = -1;
14
15 while (left <= right) {
16 int mid = left + (right - left) / 2;
17
18 // Determine if feasible: target is at mid or should be to its left
19 boolean feasible;
20 if (nums[0] <= nums[mid]) {
21 // Left half is sorted
22 feasible = nums[0] <= target && target <= nums[mid];
23 } else {
24 // Right half is sorted
25 // Feasible if target is NOT in the sorted right half
26 feasible = !(nums[mid] < target && target <= nums[n - 1]);
27 }
28
29 if (feasible) {
30 firstTrueIndex = mid;
31 right = mid - 1;
32 } else {
33 left = mid + 1;
34 }
35 }
36
37 // Check if the element at firstTrueIndex is the target
38 if (firstTrueIndex == -1) {
39 return -1;
40 }
41 return nums[firstTrueIndex] == target ? firstTrueIndex : -1;
42 }
43}
441class 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 int firstTrueIndex = -1;
8
9 while (left <= right) {
10 int mid = left + (right - left) / 2;
11
12 // Determine if feasible: target is at mid or should be to its left
13 bool feasible;
14 if (nums[0] <= nums[mid]) {
15 // Left half is sorted
16 feasible = nums[0] <= target && target <= nums[mid];
17 } else {
18 // Right half is sorted
19 // Feasible if target is NOT in the sorted right half
20 feasible = !(nums[mid] < target && target <= nums[n - 1]);
21 }
22
23 if (feasible) {
24 firstTrueIndex = mid;
25 right = mid - 1;
26 } else {
27 left = mid + 1;
28 }
29 }
30
31 // Check if the element at firstTrueIndex is the target
32 if (firstTrueIndex == -1) {
33 return -1;
34 }
35 return nums[firstTrueIndex] == target ? firstTrueIndex : -1;
36 }
37};
381/**
2 * Searches for a target value in a rotated sorted array using the binary search template.
3 *
4 * @param nums - The rotated sorted array to search in
5 * @param target - The target value to find
6 * @returns The index of the target if found, otherwise -1
7 */
8function search(nums: number[], target: number): number {
9 const n: number = nums.length;
10 let left: number = 0;
11 let right: number = n - 1;
12 let firstTrueIndex: number = -1;
13
14 while (left <= right) {
15 const mid: number = Math.floor((left + right) / 2);
16
17 // Determine if feasible: target is at mid or should be to its left
18 let feasible: boolean;
19 if (nums[0] <= nums[mid]) {
20 // Left half is sorted
21 feasible = nums[0] <= target && target <= nums[mid];
22 } else {
23 // Right half is sorted
24 // Feasible if target is NOT in the sorted right half
25 feasible = !(nums[mid] < target && target <= nums[n - 1]);
26 }
27
28 if (feasible) {
29 firstTrueIndex = mid;
30 right = mid - 1;
31 } else {
32 left = mid + 1;
33 }
34 }
35
36 // Check if the element at firstTrueIndex is the target
37 if (firstTrueIndex === -1) {
38 return -1;
39 }
40 return nums[firstTrueIndex] === target ? firstTrueIndex : -1;
41}
42Solution Approach
We use the standard binary search template with first_true_index to track our answer. The key insight is defining a feasible function that tells us whether the target could be at or before the current position.
Template Structure:
left, right = 0, n - 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index if first_true_index != -1 and nums[first_true_index] == target else -1
Defining the Feasible Function:
The feasible function returns True when the target is at index mid or should be to its left. This depends on which half is sorted:
-
If left half is sorted (
nums[0] <= nums[mid]):- Feasible if
nums[0] <= target <= nums[mid](target is in sorted left half)
- Feasible if
-
If right half is sorted (
nums[0] > nums[mid]):- Feasible if target is NOT in the sorted right half
- That is, NOT (
nums[mid] < target <= nums[n-1])
Why This Works:
When feasible returns True, we save the current mid as a potential answer and search left for an earlier occurrence. When feasible returns False, the target must be to the right.
After the loop, first_true_index holds the position where our feasible condition first became true. We then verify that nums[first_true_index] == target to confirm the target exists.
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 EvaluatorExample 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,first_true_index = -1,target = 0
Iteration 1:
- Calculate
mid = (0 + 6) // 2 = 3 nums[mid] = 7- Check if left half is sorted:
nums[0] = 4 <= nums[3] = 7✓ - Is target in sorted left half? Check if
4 <= 0 <= 7: No (0 < 4) - Feasible = False →
left = mid + 1 = 4
Iteration 2:
left = 4,right = 6- Calculate
mid = (4 + 6) // 2 = 5 nums[mid] = 1- Check if left half is sorted:
nums[0] = 4 <= nums[5] = 1? No - Right half is sorted. Is target in sorted right half?
1 < 0 <= 2? No (0 < 1) - Feasible = True →
first_true_index = 5,right = mid - 1 = 4
Iteration 3:
left = 4,right = 4- Calculate
mid = (4 + 4) // 2 = 4 nums[mid] = 0- Check if left half is sorted:
nums[0] = 4 <= nums[4] = 0? No - Right half is sorted. Is target in sorted right half?
0 < 0 <= 2? No (0 is not > 0) - Feasible = True →
first_true_index = 4,right = mid - 1 = 3
Iteration 4:
left = 4,right = 3- Loop condition
left <= rightis false, exit loop
Final Check:
first_true_index = 4- Does
nums[4] == target? Yes,0 == 0✓ - Return
4
The algorithm successfully finds the target at index 4, demonstrating the O(log n) efficiency with the standard binary search template.
Time and Space Complexity
Time Complexity: O(log n), where n is the length of the array nums. The algorithm uses the binary search template with while left <= right. In each iteration, the search space is reduced by half (either updating left = mid + 1 or right = mid - 1), 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, mid, and first_true_index, regardless of the input size. No additional data structures or recursive calls are used.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
The Pitfall:
Using while left < right with right = mid instead of the standard template with first_true_index tracking. This variant can be confusing and error-prone.
Example of the Issue:
# Non-standard variant (harder to reason about) while left < right: mid = (left + right) >> 1 if condition: right = mid else: left = mid + 1 return left if nums[left] == target else -1
Solution: Use the standard binary search template with explicit answer tracking:
left, right = 0, n - 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index if first_true_index != -1 and nums[first_true_index] == target else -1
2. Incorrect Boundary Comparison When Determining the Sorted Half
The Pitfall:
Using strict inequality (nums[0] < nums[mid]) instead of nums[0] <= nums[mid] when determining which half is sorted.
Example of the Issue:
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 < 3is false, incorrectly assuming right half is sorted - With correct comparison:
3 <= 3is true, correctly identifying left half pattern
Solution:
Always use <= to properly handle all cases:
if nums[0] <= nums[mid]: # Correct # Left half is sorted
3. Off-by-One Errors in Feasible Function
The Pitfall: When checking if the target falls within a sorted range, using incorrect comparison operators.
Example of the Issue:
# Wrong comparison for right half feasible = not (nums[mid] <= target <= nums[n - 1]) # Includes mid incorrectly
Solution: Use the correct comparisons:
# For left sorted half (target at or before mid): feasible = nums[0] <= target <= nums[mid] # For right sorted half (target NOT in right half): feasible = not (nums[mid] < target <= nums[n - 1]) # Note: strictly < for mid
4. Forgetting to Validate first_true_index
The Pitfall:
Not checking if first_true_index is still -1 (no valid position found) or if the element at that position equals the target.
Example of the Issue:
# Missing validation return first_true_index # Wrong! Might return -1 or wrong index
Solution: Always validate after the loop:
if first_true_index == -1: return -1 return first_true_index if nums[first_true_index] == target else -1
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!