🧠
Modified Binary Search with Shrinking Both Ends
💡 This approach introduces binary search logic adapted to handle duplicates by shrinking both ends when duplicates obscure the sorted half.
Intuition
Use binary search to find the target, but when duplicates at start, mid, and end prevent deciding which half is sorted, shrink the search space from both ends.
Algorithm
- Initialize two pointers, left and right, at the array ends.
- While left <= right, calculate mid index.
- If nums[mid] equals target, return true.
- If nums[left], nums[mid], and nums[right] are equal, increment left and decrement right to shrink duplicates.
- Else if left half is sorted (nums[left] <= nums[mid]), check if target lies in left half; adjust pointers accordingly.
- Else, right half is sorted; check if target lies there; adjust pointers accordingly.
- If loop ends without finding target, return false.
💡 The key challenge is handling duplicates that hide the sorted half; shrinking both ends helps restore binary search logic.
def search(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return True
if nums[left] == nums[mid] == nums[right]:
left += 1
right -= 1
elif 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
# Driver code
if __name__ == '__main__':
print(search([2,5,6,0,0,1,2], 0)) # True
print(search([2,5,6,0,0,1,2], 3)) # False
Line Notes
left, right = 0, len(nums) - 1Initialize pointers to cover entire array
mid = (left + right) // 2Calculate middle index to split search space
if nums[mid] == target:Check if mid element is target for immediate success
if nums[left] == nums[mid] == nums[right]:Duplicates at both ends and mid obscure sorted half, so shrink search space by moving both pointers inward
public class Solution {
public boolean search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return true;
if (nums[left] == nums[mid] && nums[mid] == nums[right]) {
left++;
right--;
} else if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return false;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.search(new int[]{2,5,6,0,0,1,2}, 0)); // true
System.out.println(sol.search(new int[]{2,5,6,0,0,1,2}, 3)); // false
}
}
Line Notes
int left = 0, right = nums.length - 1;Initialize search boundaries
int mid = left + (right - left) / 2;Calculate mid index safely to avoid overflow
if (nums[mid] == target) return true;Return true immediately if target found at mid
if (nums[left] == nums[mid] && nums[mid] == nums[right])Duplicates at edges and mid require shrinking search space by moving both pointers inward
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
bool search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return true;
if (nums[left] == nums[mid] && nums[mid] == nums[right]) {
left++;
right--;
} else if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return false;
}
};
int main() {
Solution sol;
vector<int> nums1 = {2,5,6,0,0,1,2};
cout << boolalpha << sol.search(nums1, 0) << endl; // true
cout << boolalpha << sol.search(nums1, 3) << endl; // false
return 0;
}
Line Notes
int left = 0, right = nums.size() - 1;Set initial search boundaries
int mid = left + (right - left) / 2;Calculate mid index to avoid overflow
if (nums[mid] == target) return true;Return true if target found at mid
if (nums[left] == nums[mid] && nums[mid] == nums[right])Duplicates at edges and mid require shrinking search space by moving both pointers inward
function search(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] === target) return true;
if (nums[left] === nums[mid] && nums[mid] === nums[right]) {
left++;
right--;
} else if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return false;
}
// Test cases
console.log(search([2,5,6,0,0,1,2], 0)); // true
console.log(search([2,5,6,0,0,1,2], 3)); // false
Line Notes
let left = 0, right = nums.length - 1;Initialize pointers to array boundaries
let mid = Math.floor((left + right) / 2);Calculate middle index for binary search
if (nums[mid] === target) return true;Return true immediately if target found at mid
if (nums[left] === nums[mid] && nums[mid] === nums[right])Duplicates at edges and mid require shrinking search space by moving both pointers inward
TimeO(n) in worst case due to duplicates, average O(log n)
SpaceO(1)
Duplicates can degrade binary search to linear time in worst case, but typically it runs in logarithmic time.
💡 For large arrays with many duplicates, this approach might scan many elements, but usually it is much faster than brute force.
Interview Verdict: Accepted and optimal for duplicates
This is the best practical approach for this problem, balancing correctness and efficiency.