title: Search in Rotated Sorted Array slug: search-in-rotated-sorted-array difficulty: medium leetcode_id: 33 leetcode_url: https://leetcode.com/problems/search-in-rotated-sorted-array/ categories: - arrays - binary-search patterns: - binary-search function_signature: "def search(nums: list[int], target: int) -> int:" test_cases: visible: - input: { nums: [4, 5, 6, 7, 0, 1, 2], target: 0 } expected: 4 - input: { nums: [4, 5, 6, 7, 0, 1, 2], target: 3 } expected: -1 - input: { nums: [1], target: 0 } expected: -1 hidden: - input: { nums: [1], target: 1 } expected: 0 - input: { nums: [3, 1], target: 1 } expected: 1 - input: { nums: [5, 1, 3], target: 5 } expected: 0 - input: { nums: [1, 2, 3, 4, 5], target: 3 } expected: 2 - input: { nums: [6, 7, 8, 1, 2, 3, 4, 5], target: 8 } expected: 2 - input: { nums: [3, 4, 5, 6, 7, 8, 1, 2], target: 2 } expected: 7 - input: { nums: [2, 3, 4, 5, 6, 7, 8, 1], target: 6 } expected: 4 description: | There is an integer array `nums` sorted in ascending order (with **distinct** values). Prior to being passed to your function, `nums` is **possibly rotated** at an unknown pivot index `k` (`1 <= k < nums.length`) such that the resulting array is `[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]` (0-indexed). For example, `[0,1,2,4,5,6,7]` might be rotated at pivot index `3` and become `[4,5,6,7,0,1,2]`. Given the array `nums` **after** the possible rotation and an integer `target`, return *the index of* `target` *if it is in* `nums`*, or* `-1` *if it is not in* `nums`. You must write an algorithm with **O(log n)** runtime complexity. constraints: | - `1 <= nums.length <= 5000` - `-10^4 <= nums[i] <= 10^4` - All values of `nums` are **unique** - `nums` is an ascending array that is possibly rotated - `-10^4 <= target <= 10^4` examples: - input: "nums = [4,5,6,7,0,1,2], target = 0" output: "4" explanation: "The target 0 is found at index 4." - input: "nums = [4,5,6,7,0,1,2], target = 3" output: "-1" explanation: "The target 3 is not in the array, so return -1." - input: "nums = [1], target = 0" output: "-1" explanation: "Single element array doesn't contain the target." explanation: intuition: | Imagine a sorted array as a staircase going upward. When you rotate it, you're cutting the staircase at some point and moving the upper portion to the end — creating a "broken" staircase with two ascending sections. For example, `[0,1,2,4,5,6,7]` becomes `[4,5,6,7,0,1,2]`. We now have two sorted "halves": `[4,5,6,7]` (the higher half) and `[0,1,2]` (the lower half). The key insight is: **at least one half of the array is always properly sorted**. When you pick a middle element, you can determine which half is sorted by comparing the endpoints. Then you check if your target falls within that sorted range — if yes, search there; if not, search the other half. Think of it like this: you're at the middle of a broken staircase. You look left and right. One side will be a clean, unbroken staircase (sorted). You can quickly tell if your target is on that sorted side. If not, it must be on the "broken" side, so you continue searching there. approach: | We solve this using **Modified Binary Search**: **Step 1: Initialise pointers** - `left = 0`, `right = len(nums) - 1` - We'll search within `[left, right]` inclusive   **Step 2: Binary search with sorted-half detection** - While `left <= right`: - Calculate `mid = left + (right - left) // 2` - If `nums[mid] == target`: return `mid` - **Determine which half is sorted**: - If `nums[left] <= nums[mid]`: **left half is sorted** - If `nums[left] <= target < nums[mid]`: target is in left half, set `right = mid - 1` - Else: target is in right half, set `left = mid + 1` - Else: **right half is sorted** - If `nums[mid] < target <= nums[right]`: target is in right half, set `left = mid + 1` - Else: target is in left half, set `right = mid - 1`   **Step 3: Return -1 if not found** - If the loop exits without finding the target, return `-1`   The algorithm works because we can always identify one sorted half and determine if the target lies within that range. If it does, we search there; otherwise, we search the other (potentially rotated) half. common_pitfalls: - title: Confusing Which Half is Sorted description: | The condition `nums[left] <= nums[mid]` (with `<=`, not `<`) correctly identifies when the left half is sorted. The `=` is important for cases where `left == mid` (small subarrays). For example, with `[3, 1]` and `left = 0, mid = 0`: - `nums[left] == nums[mid] == 3` - The left "half" (just element 3) is trivially sorted Using `<` instead of `<=` would incorrectly identify the right half as sorted. wrong_approach: "if nums[left] < nums[mid] (missing equality)" correct_approach: "if nums[left] <= nums[mid]" - title: Incorrect Target Range Checks description: | When checking if the target is in the sorted half, be precise about the boundaries: - For left half: `nums[left] <= target < nums[mid]` (exclude mid since we already checked it) - For right half: `nums[mid] < target <= nums[right]` (exclude mid) Missing the equality check with the endpoints can cause you to search the wrong half. wrong_approach: "nums[left] < target < nums[mid]" correct_approach: "nums[left] <= target < nums[mid]" - title: Using Two-Pass Approach description: | A tempting approach is to first find the pivot (minimum element) using "Find Minimum in Rotated Sorted Array", then do a standard binary search on the correct half. While this works, it requires **two passes** (O(log n) + O(log n)). The single-pass approach is cleaner and more elegant — you determine the sorted half and search direction simultaneously in one loop. wrong_approach: "Find pivot first, then binary search" correct_approach: "Single binary search with sorted-half detection" key_takeaways: - "**One sorted half**: In a rotated sorted array, at least one half is always properly sorted — use this property to guide your search" - "**Compare with endpoints**: Comparing `nums[left]` with `nums[mid]` tells you which half is sorted" - "**Range membership**: Once you identify the sorted half, checking if target is in that range is straightforward" - "**Foundation problem**: This technique is the basis for many rotated array problems (find minimum, search with duplicates, etc.)" time_complexity: "O(log n). Each iteration eliminates half the search space." space_complexity: "O(1). Only a constant number of pointer variables are used." solutions: - approach_name: Binary Search with Sorted-Half Detection is_optimal: true code: | def search(nums: list[int], target: int) -> int: left, right = 0, len(nums) - 1 while left <= right: mid = left + (right - left) // 2 # Found the target if nums[mid] == target: return mid # Determine which half is sorted if nums[left] <= nums[mid]: # Left half is sorted if nums[left] <= target < nums[mid]: # Target is in the sorted left half right = mid - 1 else: # Target is in the right half left = mid + 1 else: # Right half is sorted if nums[mid] < target <= nums[right]: # Target is in the sorted right half left = mid + 1 else: # Target is in the left half right = mid - 1 # Target not found return -1 explanation: | **Time Complexity:** O(log n) — Search space halves each iteration. **Space Complexity:** O(1) — Constant extra space. At each step, we identify which half is sorted (by comparing `nums[left]` with `nums[mid]`), then check if the target falls within that sorted range. If yes, we search that half; otherwise, we search the other half. This ensures we always make progress toward the target or confirm its absence. - approach_name: Two-Pass (Find Pivot + Binary Search) is_optimal: false code: | def search(nums: list[int], target: int) -> int: n = len(nums) # Step 1: Find the pivot (index of minimum element) left, right = 0, n - 1 while left < right: mid = left + (right - left) // 2 if nums[mid] > nums[right]: left = mid + 1 else: right = mid pivot = left # Step 2: Determine which half to search # If target >= nums[0], it's in the left portion (before pivot) # Otherwise, it's in the right portion (pivot to end) if target >= nums[0]: left, right = 0, pivot - 1 if pivot > 0 else 0 else: left, right = pivot, n - 1 # Handle edge case: array not rotated or single element if pivot == 0: left, right = 0, n - 1 # Step 3: Standard binary search in the chosen range while left <= right: mid = left + (right - left) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return -1 explanation: | **Time Complexity:** O(log n) — Two binary searches, each O(log n). **Space Complexity:** O(1) — Constant extra space. This approach first finds the pivot (minimum element) to understand the array's structure, then performs a standard binary search on the appropriate half. While correct and having the same complexity, it's more verbose than the single-pass approach. Useful if you've already solved "Find Minimum in Rotated Sorted Array" and want to build on that. - approach_name: Linear Search is_optimal: false code: | def search(nums: list[int], target: int) -> int: # Simple scan through the array for i, num in enumerate(nums): if num == target: return i return -1 explanation: | **Time Complexity:** O(n) — Scans every element. **Space Complexity:** O(1) — No extra space used. A straightforward linear scan. This doesn't meet the O(log n) requirement but demonstrates the baseline approach. With constraints up to `n = 5000`, linear search would still pass but isn't optimal.