Pivoted Array Target Search: Binary search in a rotated sorted array
You're in an Amazon onsite and the interviewer draws an array on the whiteboard: [4,5,6,7,0,1,2]. "This was sorted, then rotated at some unknown point. Find the index of a given target in O(log n) time." You recognize this immediately. This is a rotated sorted array search problem, also known as Search in Rotated Sorted Array on other platforms like LeetCode. It appears frequently at Adobe, Meta, Apple, Google, and Amazon, and it tests whether you can adapt binary search when the sorted order is disrupted by a rotation.
TL;DR
Use a modified binary search. At each step, determine which half of the array is sorted by comparing nums[left] with nums[mid]. If the target falls within the sorted half's range, search there. Otherwise, search the other half. Each iteration still eliminates half the elements, giving O(log n) time and O(1) space.
Prefer a different language? Jump to solutions in other languages.
Understanding the Rotation
A sorted array like [0,1,2,4,5,6,7] gets rotated at some pivot, producing something like [4,5,6,7,0,1,2]. The key observation: the rotated array consists of two sorted subarrays joined at the pivot point.
Loading visualization...
Original sorted array: [0, 1, 2, 4, 5, 6, 7]
Loading visualization...
After rotation at index 3: [4, 5, 6, 7, 0, 1, 2]. The highlighted boundary marks where the two sorted segments meet.
The pivot is where the order breaks. Elements before it form one sorted segment, elements after form another. Standard binary search fails because the midpoint comparison can't reliably tell you which direction to go. But with one extra check per iteration, you can recover the O(log n) guarantee.
The Core Insight
In any subarray of a rotated sorted array, at least one half is always sorted. When you compute mid, either the left half [left..mid] or the right half [mid..right] is in ascending order. You can determine which by a single comparison:
- If
nums[left] <= nums[mid], the left half is sorted. - Otherwise, the right half is sorted.
Once you know which half is sorted, checking whether the target falls within that half's range is a straightforward bounds check. If it does, search there. If not, search the other half.
Loading visualization...
This decision tree runs at every iteration. The sorted-half identification adds O(1) work per step, so the overall complexity stays O(log n).
Walkthrough: Target = 2
For [4,5,6,7,0,1,2] with target 2:
Loading visualization...
Step 0: mid=3, nums[3]=7. Left half [4,5,6,7] is sorted (4 <= 7). Target 2 is not in [4, 7), so we search right.
Step 1: mid=5, nums[5]=1. Right half [1,2] is sorted (1 <= 2). Target 2 is not in (1, 2]... wait, actually 2 <= 2 is true, but 1 < 2 is also true, so 2 IS in (1, 2]. The target is at the boundary. We search right: left = 6.
Step 2: mid=6, nums[6]=2. Match! Return index 6.
Walkthrough: Target = 0
Loading visualization...
Step 0: Left half [4,5,6,7] is sorted. Target 0 is not in [4, 7), so search right.
Step 1: Right half [1,2] is sorted. Target 0 is not in (1, 2], so search left: right = 4.
Step 2: left=4, right=4, mid=4. nums[4] = 0. Match! Return index 4.
Walkthrough: Target = 3 (Not Found)
Loading visualization...
The search narrows to a single element that doesn't match, then left crosses right. Return -1.
Implementation
public class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
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 -1;
}
}
Key implementation details:
mid = left + (right - left) / 2avoids integer overflow compared to(left + right) / 2.nums[left] <= nums[mid]uses<=(not<) to handle the case whereleft == mid, which occurs when only two elements remain.- The boundary checks are asymmetric on purpose:
nums[left] <= target && target < nums[mid]for the left half, andnums[mid] < target && target <= nums[right]for the right half. This ensures the target is strictly within the sorted half's range (excludingmiditself, which was already checked).
Walkthrough with a Larger Example
For [2,3,4,5,6,7,0,1] with target 1:
Loading visualization...
Four steps to find target 1 at index 7 in an 8-element array. Standard binary search on 8 elements takes at most log2(8) = 3 comparisons, so the rotated version adds at most one extra step due to the pivot crossing.
Complexity Analysis
Time: O(log n)
Each iteration eliminates half the remaining elements, identical to standard binary search. The extra comparison to determine which half is sorted is O(1), so it doesn't affect the logarithmic bound.
Space: O(1)
Three integer variables: left, right, and mid. No recursion, no auxiliary data structures.
Why Not Find the Pivot First?
A two-pass approach finds the pivot (rotation point) first with one binary search, then runs standard binary search on the correct half. This also runs in O(log n) time, but it requires two passes and more complex pivot-finding logic. The single-pass approach is cleaner, handles edge cases more naturally, and is what interviewers expect.
Edge Cases
-
No rotation. The array is fully sorted.
nums[left] <= nums[mid]is always true, and the algorithm reduces to standard binary search. -
Single element.
left == right == mid. Either it matches the target or it doesn't. The loop runs once. -
Target at the pivot boundary. For
[4,5,6,7,0,1,2]with target7(last element of the first segment), the algorithm correctly identifies the left half as sorted and finds7within[4, 7]. -
Two elements.
[3, 1]with target1.mid = 0,nums[0] = 3 != 1. Left half[3]is sorted. Target1is not in[3, 3), so search right.left = 1, right = 1, mid = 1. Found.
Common Mistakes
-
Using
<instead of<=innums[left] <= nums[mid]. Whenleft == mid(two-element subarray),<incorrectly identifies the left half as unsorted. This causes the algorithm to search the wrong half. -
Getting the target range checks wrong. The left-half check is
nums[left] <= target && target < nums[mid]. The right-half check isnums[mid] < target && target <= nums[right]. Mixing up the strict and non-strict inequalities is the most common source of bugs. -
Integer overflow in midpoint calculation. Always use
left + (right - left) / 2instead of(left + right) / 2. For arrays with up to 500 elements this doesn't matter in practice, but it's good habit and interviewers notice.
Interview Tips
-
Clarify constraints. "Are all elements unique? Can the array be unrotated?" Both matter for correctness. This problem guarantees unique elements; with duplicates, the worst case degrades to O(n).
-
Start with the observation. "In a rotated sorted array, at least one half around any midpoint is always sorted." This single sentence shows you understand the core idea.
-
Draw the array. Sketch
[4,5,6,7,0,1,2]on the whiteboard and markleft,mid,right. Walk through one or two iterations showing how the pointers move. -
Compare with standard binary search. "This is binary search with one extra check per iteration to determine which half is sorted. The time complexity stays O(log n)."
-
Mention the duplicate variant. If the interviewer asks a follow-up, explain that duplicates (e.g.,
[2,5,6,0,0,1,2]) break thenums[left] <= nums[mid]check because equal values don't tell you which side the pivot is on. The fix is to skip duplicates by incrementingleft, but this makes the worst case O(n).
Related Problems
Once you're comfortable with modified binary search on rotated arrays, try these:
- Find Minimum in Rotated Sorted Array - Same structure, but you search for the pivot point itself instead of a specific target
- Find Pair Indices in Sorted Array (Two Sum II) - Binary search / two pointers on a sorted array
- Subarray with Maximum Total (Maximum Subarray) - Another array problem that rewards recognizing structure
- Leap to the Finish Line (Jump Game) - Array traversal with a different decision pattern
These are all available on Firecode, where over 50,000 engineers have practiced for technical interviews at companies like Adobe, Meta, Google, and Amazon. Binary search is one of the most versatile patterns in coding interviews, and rotated array search is the problem that separates candidates who memorize templates from those who truly understand the technique.
Solutions in Other Languages
Python
from typing import List
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if 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 -1
JavaScript
class Solution {
search(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
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 -1;
}
}
TypeScript
class Solution {
search(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
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 -1;
}
}
C++
#include <vector>
class Solution {
public:
int search(std::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 mid;
}
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 -1;
}
};
Go
func (s *Solution) Search(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
}
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 -1
}
Scala
class Solution {
def search(nums: Array[Int], target: Int): Int = {
var left = 0
var right = nums.length - 1
while (left <= right) {
val mid = left + (right - left) / 2
if (nums(mid) == target) {
return mid
}
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
}
}
}
-1
}
}
Kotlin
class Solution {
fun search(nums: IntArray, target: Int): Int {
var left = 0
var right = nums.size - 1
while (left <= right) {
val mid = left + (right - left) / 2
if (nums[mid] == target) {
return mid
}
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 -1
}
}
Swift
class Solution {
func search(_ nums: [Int], _ target: Int) -> Int {
var left = 0
var right = nums.count - 1
while left <= right {
let mid = left + (right - left) / 2
if nums[mid] == target {
return mid
}
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 -1
}
}
Rust
impl Solution {
pub fn search(&self, nums: Vec<i32>, target: i32) -> i32 {
let mut left: i32 = 0;
let mut right: i32 = nums.len() as i32 - 1;
while left <= right {
let mid = left + (right - left) / 2;
if nums[mid as usize] == target {
return mid;
}
if nums[left as usize] <= nums[mid as usize] {
if nums[left as usize] <= target && target < nums[mid as usize] {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if nums[mid as usize] < target && target <= nums[right as usize] {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
-1
}
}
C#
public class Solution {
public int Search(int[] nums, int target) {
int left = 0;
int right = nums.Length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
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 -1;
}
}
Dart
class Solution {
int search(List<int> nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) ~/ 2;
if (nums[mid] == target) {
return mid;
}
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 -1;
}
}
PHP
class Solution {
public function search(array $nums, int $target): int {
$left = 0;
$right = count($nums) - 1;
while ($left <= $right) {
$mid = $left + intdiv($right - $left, 2);
if ($nums[$mid] === $target) {
return $mid;
}
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 -1;
}
}
Ruby
class Solution
def search(nums, target)
left = 0
right = nums.length - 1
while left <= right
mid = left + (right - left) / 2
if nums[mid] == target
return mid
end
if nums[left] <= nums[mid]
if nums[left] <= target && target < nums[mid]
right = mid - 1
else
left = mid + 1
end
else
if nums[mid] < target && target <= nums[right]
left = mid + 1
else
right = mid - 1
end
end
end
-1
end
end