Problem
A sorted array can be "rotated" by shifting some prefix of elements to the back. Given such a rotated array that may contain duplicate values, determine whether a given target appears in it.
- Input:
nums = [2,5,6,0,0,1,2],target = 0 - Output:
true - Explanation: 0 appears at indices 3 and 4.
Counter-example: nums = [2,5,6,0,0,1,2], target = 3 โ false. Even though the array contains values around 3, the value 3 itself is absent.
Intuition
The array is two sorted runs glued together at a rotation point. Binary search normally works by identifying which half is sorted, then ruling out the other. Duplicates break this: when the leftmost, middle, and rightmost values are all equal, you cannot tell which side the rotation sits on. The only safe move in that case is to shrink both ends by one and try again โ which is why worst-case performance degrades to O(n) when all elements are identical.
Approach 1 โ Linear Scan
Scan every element once. Return true on the first match, false if the loop finishes without finding the target.
- Iterate through each element in
nums. - Return
trueif the current element equalstarget. - Return
falseafter exhausting the array.
1def search(nums: list[int], target: int) -> bool:
2 for value in nums:
3 if value == target:
4 return True
5 return FalseTime: O(n) โ every element may be visited in the worst case.
Space: O(1) โ no extra memory used.
Approach 2 โ Modified Binary Search
Use binary search but add a dedicated branch for the ambiguous duplicate case: when the left, mid, and right values are all equal, shrink both ends simultaneously and continue.
- Set
left = 0,right = len(nums) - 1. - While
left <= right, computemid. - If
nums[mid] == target, returntrue. - If
nums[left] == nums[mid] == nums[right], shrink both ends (left++,right--) โ we cannot determine the sorted half. - If
nums[left] <= nums[mid], the left half[left..mid]is sorted: iftargetfalls in[nums[left], nums[mid]), moveright = mid - 1; otherwiseleft = mid + 1. - Otherwise the right half
[mid..right]is sorted: iftargetfalls in(nums[mid], nums[right]], moveleft = mid + 1; otherwiseright = mid - 1. - Return
falsewhen the window collapses without a match.
1def search(nums: list[int], target: int) -> bool:
2 left, right = 0, len(nums) - 1
3
4 while left <= right:
5 mid = (left + right) // 2
6
7 if nums[mid] == target:
8 return True
9
10 # Cannot determine which half is sorted โ shrink both ends safely
11 if nums[left] == nums[mid] == nums[right]:
12 left += 1
13 right -= 1
14 elif nums[left] <= nums[mid]: # left half [left..mid] is the sorted portion
15 if nums[left] <= target < nums[mid]:
16 right = mid - 1 # target must lie in the sorted left half
17 else:
18 left = mid + 1 # target lies in the right half
19 else: # right half [mid..right] is the sorted portion
20 if nums[mid] < target <= nums[right]:
21 left = mid + 1 # target must lie in the sorted right half
22 else:
23 right = mid - 1 # target lies in the left half
24
25 return FalseTime: O(log n) average โ degrades to O(n) only when all elements are identical.
Space: O(1) โ only a constant number of index variables.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Linear Scan | O(n) | O(1) | Small arrays, or when duplicates are so dense the O(n) worst case is inevitable |
| Modified Binary Search | O(log n) avg, O(n) worst | O(1) | Large arrays with sparse duplicates where sub-linear performance matters |
Common Mistakes
- Applying the no-duplicates version directly โ in Search in Rotated Sorted Array I,
nums[left] <= nums[mid]reliably identifies the sorted half. With duplicates, this is false whennums[left] == nums[mid]but the rotation sits inside the left half; you must check all three endpoints before committing to a side. - Shrinking only one end when ambiguous โ when all three endpoints match, skipping only
left++leaves the right boundary stale, which can incorrectly prune the right half in the next iteration. Both ends must shrink symmetrically. - Getting the range-check inequalities wrong โ the left-half check must be
nums[left] <= target < nums[mid](strict on the right), and the right-half check must benums[mid] < target <= nums[right](strict on the left), becausenums[mid]itself was already tested at the top of the loop. - Using strict
<for the sorted-half condition โ the conditionnums[left] <= nums[mid]must use<=because whenleft == mid(a single-element window), strict<would falsely route to the right-half branch. - Assuming O(n) worst case disqualifies binary search โ the degraded O(n) case only occurs when every element is identical, which is trivially detectable beforehand; for typical interview inputs the binary search runs in O(log n).
Related Problems
Search in Rotated Sorted Arrayโ the no-duplicates variant where binary search is always O(log n)Find Minimum in Rotated Sorted Arrayโ locating the rotation pivot using the same sorted-half identification techniqueBinary Searchโ the foundational template that all rotated-array variants extendFind First and Last Position of Element in Sorted Arrayโ binary search over duplicates to find exact left and right boundariesSingle Element in a Sorted Arrayโ uses index parity to identify which half holds the answer, a similar "which side is normal?" reasoning pattern