Binary Search in Rotated Sorted Array
Problem Description​
Given a sorted array that has been rotated at some pivot point, implement a function to find a target element in the array. The function should return the index of the target element if found, or -1 if not found.
A rotated sorted array is an array that was originally sorted in ascending order but has been rotated around a pivot point. For example:
- Original sorted array:
[1, 2, 3, 4, 5, 6, 7]
- After rotation at index 3:
[4, 5, 6, 7, 1, 2, 3]
Time Complexity​
- Time Complexity: O(log n)
- Space Complexity: O(1)
Algorithm Overview​
The algorithm uses a modified binary search approach that takes into account the rotation of the array. The key insight is that at least one half of the array (either left or right) will always be sorted.
Key Steps:​
-
Initialize two pointers:
left
pointing to the start of array (index 0)right
pointing to the end of array (index n-1)
-
While
left <= right
:- Calculate middle point:
mid = (left + right) / 2
- If target is found at mid, return mid
- Calculate middle point:
-
Check which half of the array is sorted:
- If left half is sorted (
arr[left] <= arr[mid]
):- Check if target lies in the left half
- If yes, search left half
- If no, search right half
- If right half is sorted:
- Check if target lies in the right half
- If yes, search right half
- If no, search left half
- If left half is sorted (
-
If element is not found, return -1
Implementation​
def search(nums: list[int], target: int) -> int:
if not nums:
return -1
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
# Found target
if nums[mid] == target:
return mid
# Check if left half is sorted
if nums[left] <= nums[mid]:
# Check if target is in left half
if nums[left] <= target <= nums[mid]:
right = mid - 1
else:
left = mid + 1
# Right half is sorted
else:
# Check if target is in right half
if nums[mid] <= target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
Example Usage​
# Example 1: Regular case
arr = [4, 5, 6, 7, 0, 1, 2]
target = 0
result = search(arr, target) # Returns 4
# Example 2: Target not found
arr = [4, 5, 6, 7, 0, 1, 2]
target = 3
result = search(arr, target) # Returns -1
# Example 3: Array with single element
arr = [1]
target = 1
result = search(arr, target) # Returns 0
Edge Cases to Consider​
- Empty array
- Array with single element
- Target not present in array
- Duplicate elements (not handled in basic implementation)
- Array not rotated (regular sorted array)
- Array rotated n times (back to original position)
- Target at the first or last position
Common Pitfalls​
- Not Handling Empty Arrays: Always check if the input array is empty before processing.
- Integer Overflow: When calculating mid point, use
left + (right - left) // 2
instead of(left + right) // 2
to prevent integer overflow in some languages. - Incorrect Boundary Conditions: Be careful with the conditions when checking if target lies in sorted portion.
- Duplicate Elements: The basic implementation assumes all elements are unique. Handling duplicates requires additional logic.
Applications​
- Database Indexing: When indexes are partially sorted or reorganized
- Circular Buffer Search: Finding elements in circular buffer data structures
- Version Control: Finding specific versions in circular version histories
- Resource Allocation: Finding available slots in circular resource allocation systems
Related Problems​
- Find Minimum in Rotated Sorted Array
- Search in Rotated Sorted Array II (with duplicates)
- Find Rotation Count in Rotated Sorted Array
- Find Maximum in Rotated Sorted Array
Testing Guide​
Test Cases to Cover:​
-
Basic cases:
assert search([4, 5, 6, 7, 0, 1, 2], 0) == 4
assert search([4, 5, 6, 7, 0, 1, 2], 3) == -1 -
Edge cases:
assert search([], 5) == -1
assert search([1], 1) == 0
assert search([1], 0) == -1 -
Rotation variations:
assert search([1, 2, 3, 4, 5], 4) == 3 # No rotation
assert search([2, 3, 4, 5, 1], 1) == 4 # Rotated once
assert search([5, 1, 2, 3, 4], 5) == 0 # Target at start
Performance Optimization Tips​
- Use binary search variant that avoids integer overflow
- Consider adding early termination conditions for obvious cases
- If dealing with large arrays, consider parallelization for multiple searches
- Cache frequently searched values if appropriate for your use case
Additional Resources​
- Time Complexity Analysis: Master Theorem
- Related Reading: Binary Search Trees and Their Applications
- Practice Problems: LeetCode #33, #81, #153
- Advanced Topics: Variants with duplicates, Finding multiple occurrences
Practice Problems Collection​
Essential Problems​
Problem | Difficulty | LeetCode Link | Description | Key Concepts |
---|---|---|---|---|
Search in Rotated Sorted Array | Medium | LC-33 | Find target in rotated array with unique elements | Basic rotated array search |
Search in Rotated Sorted Array II | Medium | LC-81 | Find target in rotated array with duplicates | Handling duplicates |
Find Minimum in Rotated Sorted Array | Medium | LC-153 | Find minimum element in rotated array | Modified binary search |
Find Minimum in Rotated Sorted Array II | Hard | LC-154 | Find minimum in rotated array with duplicates | Complex duplicate handling |
Related Binary Search Problems​
Problem | Difficulty | LeetCode Link | Description | Key Concepts |
---|---|---|---|---|
Peak Element | Medium | LC-162 | Find any peak element in array | Binary search on unsorted array |
Find First and Last Position | Medium | LC-34 | Find range of target element | Modified binary search |
Single Element in Sorted Array | Medium | LC-540 | Find element that appears once | Binary search with parity |
Search Insert Position | Easy | LC-35 | Find insertion position | Basic binary search |
Problem-Solving Patterns​
Pattern 1: Basic Rotated Array Search​
def findPivot(nums):
left, right = 0, len(nums) - 1
while left < right:
mid = left + (right - left) // 2
if nums[mid] > nums[right]:
left = mid + 1
else:
right = mid
return left
Pattern 2: Handling Duplicates​
def searchWithDuplicates(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
# Skip duplicates from left
while left < right and nums[left] == nums[left + 1]:
left += 1
# Skip duplicates from right
while left < right and nums[right] == nums[right - 1]:
right -= 1
# Regular binary search logic follows...
Pattern 3: Finding Range​
def searchRange(nums, target):
def findBound(nums, target, isFirst):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
if isFirst:
if mid == left or nums[mid-1] < target:
return mid
right = mid - 1
else:
if mid == right or nums[mid+1] > target:
return mid
left = mid + 1
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
return [findBound(nums, target, True),
findBound(nums, target, False)]
Problem-Solving Tips​
-
For Basic Rotated Array:
- Always check which half is sorted first
- Compare target with endpoints of sorted half
- Move to appropriate half based on comparison
-
For Duplicate Elements:
- Skip duplicate elements at boundaries
- Consider worst-case time complexity becomes O(n)
- Handle edge cases where all elements are same
-
For Finding Ranges:
- Use two binary searches for left and right bounds
- Modify condition to find first/last occurrence
- Handle cases where element doesn't exist
Common Patterns and Templates​
Template 1: Basic Binary Search in Rotated Array​
def search(nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
# Check if left half is sorted
if nums[left] <= nums[mid]:
if nums[left] <= target <= nums[mid]:
right = mid - 1
else:
left = mid + 1
# Right half is sorted
else:
if nums[mid] <= target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
Template 2: Binary Search with Duplicates​
def searchWithDuplicates(nums: List[int], target: int) -> bool:
left, right = 0, len(nums) - 1
while left <= right:
# Handle duplicates
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
mid = left + (right - left) // 2
if nums[mid] == target:
return True
if nums[left] <= nums[mid]:
if nums[left] <= target <= nums[mid]:
right = mid - 1
else:
left = mid + 1
else:
if nums[mid] <= target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return False