LC 153 with duplicates added. One extra line of code — but understanding why it's needed reveals a fundamental truth about binary search and ambiguity.
This is the same as LC 153, but the array may contain duplicate elements. Given the rotated array nums, return the minimum element. You must minimise the number of operations overall.
In LC 153, the condition nums[mid] <= nums[last] unambiguously tells us which segment mid is in. Duplicates destroy this guarantee.
Consider: [3, 3, 1, 3, 3] where last = 3. When nums[mid] == nums[last] == 3, we genuinely cannot tell whether mid is in the upper segment (before the pivot) or the lower segment (after the pivot).
When nums[mid] == nums[last], the element could be on either side of the pivot. The solution: shrink the right boundary by one and try again. We safely eliminate one candidate without losing the minimum.
The entire modification fits in a single else if branch:
1 <= 3? Yes → TRUE. Save boundary=2, search left: right=1.
3 == 3 → AMBIGUOUS. Shrink right: right=right-1=0. No boundary update.
3 == 3 → AMBIGUOUS again. Shrink right: right=-1.
Return nums[boundary] = nums[2] = 1. Correct!
Worst case degrades to O(n): If the entire array is the same value (e.g. [2,2,2,2,2]), the tie-breaking fires every iteration and we shrink by one each time. This is unavoidable — you can't distinguish a rotated from non-rotated array with all-same elements faster than O(n). The problem statement says "minimise operations", acknowledging this worst case.
class Solution { public int findMin(int[] nums) { int left = 0; int right = nums.length - 1; int boundary = 0; while (left <= right) { int mid = left + (right - left) / 2; int last = nums[nums.length - 1]; if (nums[mid] < last) { // Clearly in lower segment — could be minimum boundary = mid; right = mid - 1; } else if (nums[mid] > last) { // Clearly in upper segment — minimum is to the right left = mid + 1; } else { // nums[mid] == last: ambiguous — could be either side. // Safely shrink right boundary. We don't lose the min // because nums[right] == nums[mid] duplicates it. boundary = mid; // mid is a valid candidate right--; } } return nums[boundary]; } }
class Solution: def findMin(self, nums: list[int]) -> int: left, right = 0, len(nums) - 1 boundary = 0 while left <= right: mid = left + (right - left) // 2 last = nums[-1] if nums[mid] < last: # Clearly in lower segment — could be minimum boundary = mid right = mid - 1 elif nums[mid] > last: # Clearly in upper segment — minimum is to the right left = mid + 1 else: # nums[mid] == last: ambiguous. # Shrink right — safe because nums[right] duplicates nums[mid]. boundary = mid # mid is a valid candidate right -= 1 return nums[boundary]