Cracking The Coding Interview Together – Problem 10.3: Search in Rotated Array
Picture this: You are tuning a high-throughput microservice that caches configuration routing tables. These tables are strictly sorted by priority keys. However, during an automated failover recovery sequence, the orchestration engine performs a circular shift on the cache entries to prioritize newly elected nodes, leaving your once-pristine sorted array rotated by an unknown offset. Now, your lookup latency SLAs demand a sub-linear search time, but your standard binary search is completely blind-sided by the rotation boundary. How do we find our target key without dropping back to a brute-force linear scan? Today, we are going to unpack Problem 10.3: Search in Rotated Array from Cracking the Coding Interview. By the end of this post, we will build a robust, production-grade algorithm that conquers this data structure challenge with logarithmic efficiency.
Problem Statement
Given a sorted array of n integers that has been rotated an unknown number of times, write code to find the index of a given element in the array. You may assume that the array was originally sorted in increasing order (e.g., [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14] searched for 5 should return index 8).
Our operational goals are clear:
- Handle duplicate elements gracefully if they exist, or understand how they degrade our performance guarantees.
- Achieve an optimal time complexity—ideally $O(\log n)$ in the average case.
- Keep space complexity at $O(1)$ by avoiding unnecessary allocations.
Analyze the Problem Together
Let us break down the constraints and hunt for the "Aha!" conflict. If we look at a standard sorted array like [1, 3, 5, 7, 9, 11], splitting it down the middle gives us two monotonic halves. When we rotate the array—say, moving the last three elements to the front to get [7, 9, 11, 1, 3, 5]—that clean monotonicity shatters.
However, if you pick any arbitrary midpoint in a rotated array, at least one of the two halves must always remain strictly ordered. This is our golden insight! Even though the rotation point breaks global sorting, a local binary split guarantees that one side is a normal, pristine sorted segment while the other side contains the rotation pivot.
| Approach | Time Complexity | Space Complexity | Pros & Cons |
|---|---|---|---|
| Linear Scan | O(N) | O(1) | Simple to write, but completely ignores the sorting property. Unacceptable for large datasets. |
| Find Pivot + Binary Search | O(N) or O(log N) | O(1) | Requires an extra pass to find the minimum element, or complex boundary adjustments. |
| Modified Binary Search (Our Choice) | O(log N) avg | O(1) | Single-pass elegance. Directly inspects sorted halves and adapts on the fly. |
Questions for the Interviewer
Before we drop a single line of code, let's establish our professional engineering posture by asking critical clarifying questions:
- "Can we assume there are no duplicate values in the array, or do we need to handle duplicates which could force a worst-case $O(N)$ fallback?"
- "What should the method return if the element is missing? Should we return
-1or throw a custom exception?" - "Is the array guaranteed to be rotated, or could it be an unrotated sorted array? Does our solution handle both seamlessly?"
Think About the Solution
Our architecture centers around a modified binary search loop. We maintain left and right pointers. In each iteration:
- Calculate
mid = left + (right - left) / 2. - Check if
array[mid] == target. If yes, returnmid. - Determine which half is normally ordered:
- If
array[left] <= array[mid], the left half is sorted. - Otherwise, the right half is sorted.
- If
- Check if the target falls within the boundaries of the sorted half:
- If it does, narrow our search range to that sorted half.
- If it doesn't, search the opposite half.
Edge cases to pre-empt: Empty arrays, arrays with a single element, targets smaller than the minimum or larger than the maximum, and handling arrays with duplicate elements where array[left] == array[mid] == array[right].
Write Code and Explain
Here is our production-ready Java implementation. Notice the defensive checks and clear inline comments explaining each decision.
public class RotatedArraySearch {
public static int search(int[] arr, int target) {
if (arr == null || arr.length == 0) {
return -1;
}
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
// Found our target
if (arr[mid] == target) {
return mid;
}
// Check if the left half is sorted
if (arr[left] < arr[mid]) {
// Does the target lie within the sorted left half?
if (target >= arr[left] && target < arr[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
// Otherwise, the right half must be sorted
else if (arr[left] > arr[mid]) {
// Does the target lie within the sorted right half?
if (target > arr[mid] && target <= arr[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// Handle duplicates where arr[left] == arr[mid]
else {
// If left points to the duplicate target, we found it
if (arr[left] == target) {
return left;
}
// Otherwise, we cannot be sure which side is sorted, so shrink left
left++;
}
}
// Target not found
return -1;
}
public static void main(String[] args) {
int[] rotated = {15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14};
int target = 5;
int result = search(rotated, target);
System.out.println("Element found at index: " + result);
}
}
Walkthrough of the Code:
- We initialize
left = 0andright = arr.length - 1. - Inside the
whileloop, we calculatemidsafely to prevent integer overflow. - We verify if
arr[left] < arr[mid]. If true, the left side is cleanly ascending. We then test if ourtargetsits comfortably betweenarr[left]andarr[mid]. If it does, we discard the right half. If not, we search the right half. - If
arr[left] > arr[mid], the rotation pivot must reside in the left half, meaning the right half is cleanly sorted. We apply the symmetric boundary check. - The
elseblock handles the tricky duplicate edge case (e.g.,[2, 2, 2, 3, 2]), incrementingleftlinearly when ambiguity prevents binary elimination.
Time & Space Complexity
- Time Complexity: $O(\log n)$ in the average and best case when all elements are distinct. In the absolute worst-case scenario where the array is flooded with duplicate elements (forcing our duplicate fallback to increment
leftsequentially), the complexity degrades to $O(n)$. - Space Complexity: $O(1)$. We only allocate a few primitive integer variables (
left,right,mid), maintaining a constant memory footprint regardless of input size.
Why This Problem Matters (The Senior Perspective)
Problems like searching in a rotated array bridge abstract algorithmic theory with real-world systems engineering. Whenever you deal with sharded data stores, consistent hashing rings, or ring buffers in high-performance networking, you encounter data that is logically continuous yet physically wrapped around boundaries. Understanding how to adapt standard divide-and-conquer algorithms to handle structural anomalies without sacrificing performance is a hallmark of senior engineering judgment.
Final Thoughts
Mastering modified binary search unlocks a whole category of interview questions where standard invariants are intentionally broken. Take some time to test this code with edge cases like single-element arrays and heavy duplicates. If you enjoyed this deep dive, share it with a peer or drop a comment below with your favorite binary search variation. Until next time, happy coding!
Member discussion