153. Find Minimum in Rotated Sorted Array
Problem Description
You are given an array that was originally sorted in ascending order, but has been rotated between 1 and n
times (where n
is the length of the array).
A rotation means taking the last element and moving it to the front. For example:
- Original array:
[0,1,2,4,5,6,7]
- After 1 rotation:
[7,0,1,2,4,5,6]
- After 4 rotations:
[4,5,6,7,0,1,2]
- After 7 rotations:
[0,1,2,4,5,6,7]
(back to original)
Your task is to find and return the minimum element in this rotated sorted array.
Key requirements:
- All elements in the array are unique (no duplicates)
- Your algorithm must run in
O(log n)
time complexity
The solution uses binary search to efficiently find the minimum element. The key insight is that in a rotated sorted array, the minimum element is at the "pivot point" where the rotation occurred. The algorithm compares elements with nums[0]
to determine which half of the array contains the pivot point:
- If
nums[0] <= nums[-1]
, the array is not rotated (or rotatedn
times), so the minimum isnums[0]
- Otherwise, use binary search to find where the rotation break occurs by checking if
nums[mid]
is greater than or equal tonums[0]
to decide which half to search next
Intuition
When we look at a rotated sorted array, we can observe a key pattern: the array can be divided into two sorted portions. For example, in [4,5,6,7,0,1,2]
, we have [4,5,6,7]
and [0,1,2]
- both sorted, but there's a "break" between them where the values drop from 7 to 0.
The minimum element will always be at this break point - specifically, it's the first element of the second sorted portion.
Since we need O(log n)
time complexity, we can't scan through the entire array. This constraint immediately suggests binary search.
The challenge is: how do we know which half to search? Here's the key insight - we can use the first element nums[0]
as a reference point:
-
If the array hasn't been rotated (or rotated exactly
n
times), thennums[0] <= nums[-1]
, andnums[0]
is already the minimum. -
If the array is rotated, then
nums[0]
belongs to the "upper" sorted portion (the larger values). Any element in this upper portion will be>= nums[0]
, while elements in the "lower" sorted portion will be< nums[0]
. -
During binary search, when we look at
nums[mid]
:- If
nums[mid] >= nums[0]
, thenmid
is in the upper portion, so the minimum must be to the right (setleft = mid + 1
) - If
nums[mid] < nums[0]
, thenmid
is in the lower portion where the minimum exists, butmid
itself could be the minimum (setright = mid
)
- If
This way, we progressively narrow down to the exact point where the rotation break occurs, which is where our minimum element resides.
Solution Approach
Let's walk through the implementation step by step:
Step 1: Check if the array is rotated
if nums[0] <= nums[-1]: return nums[0]
First, we check if the first element is less than or equal to the last element. If true, the array hasn't been rotated (or has been rotated exactly n
times back to its original position), so the minimum is simply nums[0]
.
Step 2: Initialize binary search pointers
left, right = 0, len(nums) - 1
We set up two pointers for binary search - left
starting at index 0 and right
at the last index.
Step 3: Binary search loop
while left < right: mid = (left + right) >> 1
We continue searching while left < right
. The middle index is calculated using bit shift operation >> 1
(equivalent to dividing by 2), which is slightly more efficient than (left + right) // 2
.
Step 4: Determine which half contains the minimum
if nums[0] <= nums[mid]: left = mid + 1 else: right = mid
This is the core logic:
- If
nums[0] <= nums[mid]
, it meansmid
is in the upper sorted portion (before the rotation break). The minimum must be in the right half, so we moveleft
tomid + 1
. - If
nums[0] > nums[mid]
, it meansmid
is in the lower sorted portion (after the rotation break). The minimum could be atmid
itself or to its left, so we setright = mid
(notmid - 1
to preserve the potential minimum).
Step 5: Return the result
return nums[left]
When the loop exits (left == right
), both pointers converge at the minimum element's position.
Time Complexity: O(log n)
- We eliminate half of the search space in each iteration.
Space Complexity: O(1)
- We only use a constant amount of extra space for the pointers.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the algorithm with the array [4,5,6,7,0,1,2]
.
Initial Check:
nums[0] = 4
andnums[-1] = 2
- Since
4 > 2
, the array is rotated, so we proceed with binary search
Binary Search Process:
Iteration 1:
left = 0, right = 6
mid = (0 + 6) >> 1 = 3
nums[mid] = nums[3] = 7
- Compare:
nums[0] = 4 <= nums[3] = 7
✓ - This means index 3 is in the upper sorted portion
[4,5,6,7]
- The minimum must be to the right of mid
- Update:
left = mid + 1 = 4
Iteration 2:
left = 4, right = 6
mid = (4 + 6) >> 1 = 5
nums[mid] = nums[5] = 1
- Compare:
nums[0] = 4 <= nums[5] = 1
✗ - This means index 5 is in the lower sorted portion
[0,1,2]
- The minimum could be at mid or to its left
- Update:
right = mid = 5
Iteration 3:
left = 4, right = 5
mid = (4 + 5) >> 1 = 4
nums[mid] = nums[4] = 0
- Compare:
nums[0] = 4 <= nums[4] = 0
✗ - Index 4 is in the lower sorted portion
- The minimum could be at mid itself
- Update:
right = mid = 4
Termination:
left = 4, right = 4
(converged)- Return
nums[4] = 0
The algorithm correctly identifies that the minimum element 0
is at index 4, which is exactly where the rotation break occurs (between 7
and 0
).
Solution Implementation
1class Solution:
2 def findMin(self, nums: List[int]) -> int:
3 # If array is not rotated or has only one element, first element is minimum
4 if nums[0] <= nums[-1]:
5 return nums[0]
6
7 # Initialize binary search boundaries
8 left, right = 0, len(nums) - 1
9
10 # Binary search to find the rotation point (minimum element)
11 while left < right:
12 # Calculate middle index using bit shift (equivalent to // 2)
13 mid = (left + right) >> 1
14
15 # If mid element is greater than or equal to first element,
16 # the minimum must be in the right half (after rotation point)
17 if nums[0] <= nums[mid]:
18 left = mid + 1
19 # Otherwise, the minimum is in the left half (including mid)
20 else:
21 right = mid
22
23 # When left == right, we've found the minimum element
24 return nums[left]
25
1class Solution {
2 /**
3 * Finds the minimum element in a rotated sorted array.
4 * Uses binary search to find the pivot point where the rotation occurs.
5 *
6 * @param nums - rotated sorted array (may not be rotated)
7 * @return the minimum element in the array
8 */
9 public int findMin(int[] nums) {
10 int n = nums.length;
11
12 // If array is not rotated (first element <= last element),
13 // the minimum is the first element
14 if (nums[0] <= nums[n - 1]) {
15 return nums[0];
16 }
17
18 // Binary search to find the rotation point
19 int left = 0;
20 int right = n - 1;
21
22 while (left < right) {
23 // Calculate middle index using bit shift (equivalent to (left + right) / 2)
24 int mid = (left + right) >> 1;
25
26 // If middle element is greater than or equal to first element,
27 // the minimum must be in the right half
28 if (nums[0] <= nums[mid]) {
29 left = mid + 1;
30 } else {
31 // Otherwise, the minimum is in the left half (including mid)
32 right = mid;
33 }
34 }
35
36 // When left == right, we've found the minimum element
37 return nums[left];
38 }
39}
40
1class Solution {
2public:
3 int findMin(vector<int>& nums) {
4 int n = nums.size();
5
6 // If array is not rotated (first element <= last element), minimum is at index 0
7 if (nums[0] <= nums[n - 1]) {
8 return nums[0];
9 }
10
11 // Binary search to find the rotation point (minimum element)
12 int left = 0;
13 int right = n - 1;
14
15 while (left < right) {
16 // Calculate middle index using bit shift (equivalent to dividing by 2)
17 int mid = (left + right) >> 1;
18
19 // If mid element is in the left sorted portion (greater than or equal to first element),
20 // the minimum must be in the right half
21 if (nums[0] <= nums[mid]) {
22 left = mid + 1;
23 }
24 // Otherwise, the minimum is in the left half (including mid itself)
25 else {
26 right = mid;
27 }
28 }
29
30 // After the loop, left points to the minimum element
31 return nums[left];
32 }
33};
34
1/**
2 * Finds the minimum element in a rotated sorted array
3 * Uses binary search to achieve O(log n) time complexity
4 * @param nums - The rotated sorted array
5 * @returns The minimum element in the array
6 */
7function findMin(nums: number[]): number {
8 // Initialize left pointer to the start of array
9 let left: number = 0;
10
11 // Initialize right pointer to the end of array
12 let right: number = nums.length - 1;
13
14 // Continue searching while the search space has more than one element
15 while (left < right) {
16 // Calculate middle index using unsigned right shift to avoid overflow
17 // Equivalent to Math.floor((left + right) / 2)
18 const mid: number = (left + right) >>> 1;
19
20 // If middle element is greater than rightmost element,
21 // the minimum must be in the right half (after mid)
22 if (nums[mid] > nums[right]) {
23 left = mid + 1;
24 } else {
25 // Otherwise, the minimum is in the left half (including mid)
26 // or mid itself could be the minimum
27 right = mid;
28 }
29 }
30
31 // When left equals right, we've found the minimum element
32 return nums[left];
33}
34
Time and Space Complexity
Time Complexity: O(log n)
, where n
is the length of the input array nums
.
The algorithm uses binary search to find the minimum element in a rotated sorted array. In each iteration of the while loop, the search space is reduced by half. The number of iterations is logarithmic with respect to the array size, as we divide the search range by 2 in each step until left >= right
. The initial check if nums[0] <= nums[-1]
takes O(1)
time, and all operations inside the loop (comparison and index calculations) are O(1)
. Therefore, the overall time complexity is O(log n)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space. It maintains three integer variables (left
, right
, and mid
) regardless of the input size. No additional data structures are created, and the algorithm operates directly on the input array without recursion (which would use stack space). Thus, the space complexity is constant.
Common Pitfalls
1. Incorrect Boundary Update When Moving Right Pointer
The Pitfall:
A common mistake is setting right = mid - 1
instead of right = mid
when nums[0] > nums[mid]
:
# INCORRECT if nums[0] <= nums[mid]: left = mid + 1 else: right = mid - 1 # Wrong! Could skip the minimum
Why This Fails:
When nums[0] > nums[mid]
, the element at mid
could actually be the minimum itself. By setting right = mid - 1
, we might exclude the minimum from our search range.
Example:
For array [4,5,1,2,3]
:
mid = 2
,nums[mid] = 1
- Since
nums[0] = 4 > nums[mid] = 1
, we know minimum is in left half - But
nums[mid] = 1
IS the minimum! Settingright = mid - 1
would skip it
Solution:
Always use right = mid
to preserve the potential minimum:
if nums[0] <= nums[mid]: left = mid + 1 else: right = mid # Correct: keeps mid in search range
2. Comparing with Wrong Reference Point
The Pitfall:
Using nums[left]
instead of nums[0]
as the reference for comparison:
# INCORRECT if nums[left] <= nums[mid]: # Wrong reference point! left = mid + 1 else: right = mid
Why This Fails:
As left
changes during the search, nums[left]
no longer represents the start of the rotated array. This breaks the logic for determining which half contains the rotation point.
Example:
For array [4,5,6,7,0,1,2]
:
- Initially:
left = 0
,mid = 3
,nums[left] = 4 <= nums[mid] = 7
✓ - After update:
left = 4
,mid = 5
,nums[left] = 0 <= nums[mid] = 1
✓ - This would incorrectly move
left
to 6, missing the minimum at index 4
Solution:
Always compare with the fixed reference nums[0]
:
if nums[0] <= nums[mid]: # Correct: fixed reference point left = mid + 1 else: right = mid
3. Forgetting Edge Cases
The Pitfall: Not handling arrays with single element or unrotated arrays:
# INCORRECT - jumps straight to binary search
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
# ... binary search logic
Why This Fails:
- Single element arrays would work but are inefficient
- Unrotated arrays would unnecessarily go through binary search when the answer is simply
nums[0]
Solution: Add the initial check:
if nums[0] <= nums[-1]: return nums[0]
This handles both unrotated arrays and single-element arrays efficiently in O(1) time.
What's the output of running the following function using input [30, 20, 10, 100, 33, 12]
?
1def fun(arr: List[int]) -> List[int]:
2 import heapq
3 heapq.heapify(arr)
4 res = []
5 for i in range(3):
6 res.append(heapq.heappop(arr))
7 return res
8
1public static int[] fun(int[] arr) {
2 int[] res = new int[3];
3 PriorityQueue<Integer> heap = new PriorityQueue<>();
4 for (int i = 0; i < arr.length; i++) {
5 heap.add(arr[i]);
6 }
7 for (int i = 0; i < 3; i++) {
8 res[i] = heap.poll();
9 }
10 return res;
11}
12
1class HeapItem {
2 constructor(item, priority = item) {
3 this.item = item;
4 this.priority = priority;
5 }
6}
7
8class MinHeap {
9 constructor() {
10 this.heap = [];
11 }
12
13 push(node) {
14 // insert the new node at the end of the heap array
15 this.heap.push(node);
16 // find the correct position for the new node
17 this.bubble_up();
18 }
19
20 bubble_up() {
21 let index = this.heap.length - 1;
22
23 while (index > 0) {
24 const element = this.heap[index];
25 const parentIndex = Math.floor((index - 1) / 2);
26 const parent = this.heap[parentIndex];
27
28 if (parent.priority <= element.priority) break;
29 // if the parent is bigger than the child then swap the parent and child
30 this.heap[index] = parent;
31 this.heap[parentIndex] = element;
32 index = parentIndex;
33 }
34 }
35
36 pop() {
37 const min = this.heap[0];
38 this.heap[0] = this.heap[this.size() - 1];
39 this.heap.pop();
40 this.bubble_down();
41 return min;
42 }
43
44 bubble_down() {
45 let index = 0;
46 let min = index;
47 const n = this.heap.length;
48
49 while (index < n) {
50 const left = 2 * index + 1;
51 const right = left + 1;
52
53 if (left < n && this.heap[left].priority < this.heap[min].priority) {
54 min = left;
55 }
56 if (right < n && this.heap[right].priority < this.heap[min].priority) {
57 min = right;
58 }
59 if (min === index) break;
60 [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61 index = min;
62 }
63 }
64
65 peek() {
66 return this.heap[0];
67 }
68
69 size() {
70 return this.heap.length;
71 }
72}
73
74function fun(arr) {
75 const heap = new MinHeap();
76 for (const x of arr) {
77 heap.push(new HeapItem(x));
78 }
79 const res = [];
80 for (let i = 0; i < 3; i++) {
81 res.push(heap.pop().item);
82 }
83 return res;
84}
85
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!