{x}
blog image

Search in Rotated Sorted Array II

There is an integer array nums sorted in non-decreasing order (not necessarily with distinct values).

Before being passed to your function, nums is rotated at an unknown pivot index k (0 <= 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,4,4,5,6,6,7] might be rotated at pivot index 5 and become [4,5,6,6,7,0,1,2,4,4].

Given the array nums after the rotation and an integer target, return true if target is in nums, or false if it is not in nums.

You must decrease the overall operation steps as much as possible.

 

Example 1:

Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true

Example 2:

Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false

 

Constraints:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums is guaranteed to be rotated at some pivot.
  • -104 <= target <= 104

 

Follow up: This problem is similar to Search in Rotated Sorted Array, but nums may contain duplicates. Would this affect the runtime complexity? How and why?

Solution Explanation: Search in Rotated Sorted Array II

This problem asks to determine if a target integer exists within a rotated sorted array that may contain duplicate values. The key difference from the problem without duplicates is the handling of duplicate elements, which affects the time complexity.

Approach: Modified Binary Search

The most efficient approach is a modified binary search. A standard binary search wouldn't work directly because the array is rotated. The modification handles the potential for duplicates, which makes it impossible to definitively determine which half of the array to search in certain cases.

Algorithm:

  1. Initialization: Set left pointer (l) to 0 and right pointer (r) to the last index of the array.

  2. Iteration: While left < right:

    • Calculate the middle index mid = (left + right) / 2.
    • Case 1: nums[mid] > nums[right]: This indicates that the right half of the array (mid + 1 to right) is sorted.
      • If nums[left] <= target <= nums[mid], search in the left half (left to mid).
      • Otherwise, search in the right half (mid + 1 to right).
    • Case 2: nums[mid] < nums[right]: This implies that the left half of the array (left to mid) is sorted.
      • If nums[mid] < target <= nums[right], search in the right half (mid + 1 to right).
      • Otherwise, search in the left half (left to mid).
    • Case 3: nums[mid] == nums[right]: This is the crucial case handling duplicates. We can't definitively say which half is sorted. In this situation, we simply decrement right by 1. This is because if nums[mid] == nums[right], then the last element can be safely disregarded in the next iteration. The worst-case scenario is where this condition repeats multiple times, leading to a linear time complexity in the worst case.
  3. Termination: The loop terminates when left == right. At this point, nums[left] (or nums[right]) is checked against the target. If they are equal, the target exists in the array; otherwise, it does not.

Time Complexity Analysis:

  • Best Case: O(log n). This occurs when there are no duplicates or few duplicates, and the target is found quickly. The search behaves like a standard binary search.
  • Worst Case: O(n). This happens when there are many duplicates. Consider an array like [2, 2, 2, 2, 2, 2, 2, 2, 0]. If the target is not 2, the nums[mid] == nums[right] condition will be met many times in succession, effectively making the search linear. The algorithm may end up checking most or all of the elements in the array.
  • Average Case: Difficult to precisely analyze analytically, but it will typically fall between O(log n) and O(n), depending on the distribution of duplicates.

Space Complexity: O(1). The algorithm uses a constant amount of extra space.

Code Examples (Python):

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        l, r = 0, len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                return True
            elif nums[mid] > nums[r]: #right half sorted
                if nums[l] <= target < nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            elif nums[mid] < nums[r]: #left half sorted
                if nums[mid] < target <= nums[r]:
                    l = mid + 1
                else:
                    r = mid -1
            else: #nums[mid] == nums[r] handle duplicates
                r -= 1
        return False

The provided code in other languages follows the same logic and algorithmic structure. They differ only in syntax and language-specific details.