Search in Rotated Sorted Array II (With Duplicates)

Hard

Search in Rotated Sorted Array II — Handling Duplicates

Pattern: Binary Search

Idea:


💻 Code

def search(nums, target):

    low = 0
    high = len(nums) - 1

    while low <= high:

        mid = (low + high) // 2

        if nums[mid] == target:
            return True

        # Ambiguous because duplicates hide
        # which half is sorted.
        if (
            nums[low] == nums[mid]
            and nums[mid] == nums[high]
        ):
            low += 1
            high -= 1
            continue

        # Left half is sorted.
        if nums[low] <= nums[mid]:

            if nums[low] <= target < nums[mid]:
                high = mid - 1
            else:
                low = mid + 1

        # Right half is sorted.
        else:

            if nums[mid] < target <= nums[high]:
                low = mid + 1
            else:
                high = mid - 1

    return False

Time complexity - O(n) , worst case when all duplicates Aux. Space complexity - O(1) Original (Distinct Elements) - search-in-sorted-rotated-array


Variation: Same as Search in Rotated Sorted Array, but duplicate values are allowed.

Main difficulty: duplicates can make it impossible to determine which half is sorted.


1. Normal Rotated Sorted Array

Example:

nums = [4,5,6,7,0,1,2]

With distinct elements, one half is always clearly sorted.

[4,5,6,7] | [0,1,2]
 ↑             ↑
sorted        sorted

We use:

if nums[low] <= nums[mid]:

to identify the left half as sorted.

Because elements are distinct, low == mid is the only way they can be equal, so:

nums[low] <= nums[mid]

gives useful ordering information.


2. What Changes with Duplicates?

Example:

nums = [1,1,1,3,1]

Suppose:

low = 0
mid = 2
high = 4

Then:

nums[low] = 1
nums[mid] = 1
nums[high] = 1

So:

nums[low] <= nums[mid]

is technically true:

1 <= 1

But it tells us nothing useful about where the rotation occurs.

The same boundary information can occur in arrays such as:

[1,1,1,3,1]

and

[1,3,1,1,1]

At the relevant boundaries, we can see:

low = 1
mid = 1
high = 1

Yet the position of 3 is different.

Therefore:

When nums[low] == nums[mid] == nums[high], the usual sorted-half reasoning becomes ambiguous.


3. Why Filter This Particular Case?

When:

nums[low] == nums[mid] == nums[high]

we cannot determine which half contains the rotation.

So first check:

if nums[mid] == target:
    return True

If it isn’t the target, safely shrink:

low += 1
high -= 1

Why is this safe?

Both boundary elements have the same value as mid.

If that value were the target, we would already have returned True.

If it isn’t the target, removing those duplicate copies cannot remove the target.

We’re simply discarding redundant information.


4. Complete Code

def search(nums, target):

    low = 0
    high = len(nums) - 1

    while low <= high:

        mid = (low + high) // 2

        if nums[mid] == target:
            return True

        # Ambiguous because duplicates hide
        # which half is sorted.
        if (
            nums[low] == nums[mid]
            and nums[mid] == nums[high]
        ):
            low += 1
            high -= 1
            continue

        # Left half is sorted.
        if nums[low] <= nums[mid]:

            if nums[low] <= target < nums[mid]:
                high = mid - 1
            else:
                low = mid + 1

        # Right half is sorted.
        else:

            if nums[mid] < target <= nums[high]:
                low = mid + 1
            else:
                high = mid - 1

    return False

5. Complexity

Average / typical case

Still behaves like binary search:

O(log n)

Worst case

If the array contains many duplicates:

[2,2,2,2,2,2,2, ...]

we may only be able to do:

low += 1
high -= 1

instead of discarding half the search space.

Therefore:

Worst-case Time = O(n)
Auxiliary Space = O(1)

6. The Key Interview Insight

Don’t say:

“We can’t use nums[low] <= nums[mid] when duplicates exist.”

That’s incorrect.

Instead say:

“We can still use the same sorted-half logic, but when nums[low] == nums[mid] == nums[high], the comparison gives insufficient information about which side is sorted. We therefore shrink both boundaries to remove redundant duplicates. This is what causes the worst-case complexity to degrade from O(log n) to O(n).”

Mental model

Distinct values
      ↓
One half identifiable
      ↓
Discard half
      ↓
O(log n)

Duplicates at L = M = R
      ↓
Boundary information ambiguous
      ↓
Discard only redundant endpoints
      ↓
Worst case O(n)

Local Graph View

Start typing to search
Try: two sum or #Arrays or #Amazon