Language Selection
Choose your preferred programming language
Find Minimum in Rotated Sorted Array
Problem Statement
An array of distinct integers was sorted in increasing order and then rotated: some number of elements were moved, in order, from the front to the back. For example, [1, 3, 6, 9, 12] rotated by three positions becomes [9, 12, 1, 3, 6]. Rotating by the array’s length gives back the original sorted array.
Given the rotated array nums, return its smallest element. Your algorithm must run in O(log n) time.
Examples
Example 1:
Input: nums = [6,9,12,1,3]
Output: 1
Explanation: The original array was [1,3,6,9,12], rotated so that 6 comes first.
Example 2:
Input: nums = [40,10,20,30]
Output: 10
Explanation: Only the largest value was moved to the front, so the minimum is at index 1.
Example 3:
Input: nums = [-4,0,5,8]
Output: -4
Explanation: The array is fully sorted (it was rotated by its own length), so the first element is the minimum.
Constraints
n == nums.length1 <= n <= 5000-5000 <= nums[i] <= 5000- All values in
numsare distinct. numsis a sorted array rotated between1andntimes.
Approach 1: Linear Scan for the Drop
Algorithm
In a rotated sorted array, values increase everywhere except at one spot: the point where the largest value is followed by the smallest. If there is no such drop, the array is fully sorted and the first element is the minimum.
- Walk the array from index 1.
- The first time
nums[i] < nums[i - 1], returnnums[i]. - If no drop is found, return
nums[0].
This is correct and easy to explain, but it is O(n) and does not meet the O(log n) requirement.
Implementation
Python:
from typing import List
class Solution:
def findMin(self, nums: List[int]) -> int:
# The minimum is the only element smaller than its left neighbor
for i in range(1, len(nums)):
if nums[i] < nums[i - 1]:
return nums[i]
# No drop: the array is not rotated
return nums[0]
Java:
class Solution {
public int findMin(int[] nums) {
// The minimum is the only element smaller than its left neighbor
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1]) {
return nums[i];
}
}
// No drop: the array is not rotated
return nums[0];
}
}
Go:
func findMin(nums []int) int {
// The minimum is the only element smaller than its left neighbor
for i := 1; i < len(nums); i++ {
if nums[i] < nums[i-1] {
return nums[i]
}
}
// No drop: the array is not rotated
return nums[0]
}
JavaScript:
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
// The minimum is the only element smaller than its left neighbor
for (let i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1]) {
return nums[i];
}
}
// No drop: the array is not rotated
return nums[0];
};
C#:
public class Solution {
public int FindMin(int[] nums) {
// The minimum is the only element smaller than its left neighbor
for (int i = 1; i < nums.Length; i++) {
if (nums[i] < nums[i - 1]) {
return nums[i];
}
}
// No drop: the array is not rotated
return nums[0];
}
}
C++:
#include <vector>
using namespace std;
class Solution {
public:
int findMin(vector<int>& nums) {
// The minimum is the only element smaller than its left neighbor
for (size_t i = 1; i < nums.size(); i++) {
if (nums[i] < nums[i - 1]) {
return nums[i];
}
}
// No drop: the array is not rotated
return nums[0];
}
};
Complexity Analysis
- Time: O(n) in the worst case, when the drop is near the end or the array is not rotated.
- Space: O(1).
Approach 2: Binary Search Against the Right End (Optimal)
Algorithm
Picture the rotated array as two sorted runs: a left run of larger values and a right run of smaller values, with the minimum at the start of the right run. Every value in the left run is greater than every value in the right run, including the last element nums[hi].
So comparing nums[mid] with nums[hi] tells us which run mid is in:
- If
nums[mid] > nums[hi],midis in the left run. The minimum is strictly to the right:lo = mid + 1. - Otherwise
nums[mid] < nums[hi](values are distinct), somidis in the right run. The minimum is atmidor to its left:hi = mid.
Steps:
- Set
lo = 0,hi = n - 1. - While
lo < hi, computemid = lo + (hi - lo) / 2and shrink the range using the rule above. - When
lo == hi, that index holds the minimum.
The loop keeps the minimum inside [lo, hi] at every step, and the range shrinks every iteration because mid < hi.
Implementation
Python:
from typing import List
class Solution:
def findMin(self, nums: List[int]) -> int:
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] > nums[hi]:
lo = mid + 1 # mid is in the larger left run
else:
hi = mid # mid is in the smaller right run, maybe the minimum
return nums[lo]
Java:
class Solution {
public int findMin(int[] nums) {
int lo = 0, hi = nums.length - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] > nums[hi]) {
lo = mid + 1; // mid is in the larger left run
} else {
hi = mid; // mid is in the smaller right run, maybe the minimum
}
}
return nums[lo];
}
}
Go:
func findMin(nums []int) int {
lo, hi := 0, len(nums)-1
for lo < hi {
mid := lo + (hi-lo)/2
if nums[mid] > nums[hi] {
lo = mid + 1 // mid is in the larger left run
} else {
hi = mid // mid is in the smaller right run, maybe the minimum
}
}
return nums[lo]
}
JavaScript:
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
let lo = 0;
let hi = nums.length - 1;
while (lo < hi) {
const mid = lo + ((hi - lo) >> 1);
if (nums[mid] > nums[hi]) {
lo = mid + 1; // mid is in the larger left run
} else {
hi = mid; // mid is in the smaller right run, maybe the minimum
}
}
return nums[lo];
};
C#:
public class Solution {
public int FindMin(int[] nums) {
int lo = 0, hi = nums.Length - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] > nums[hi]) {
lo = mid + 1; // mid is in the larger left run
} else {
hi = mid; // mid is in the smaller right run, maybe the minimum
}
}
return nums[lo];
}
}
C++:
#include <vector>
using namespace std;
class Solution {
public:
int findMin(vector<int>& nums) {
int lo = 0, hi = static_cast<int>(nums.size()) - 1;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (nums[mid] > nums[hi]) {
lo = mid + 1; // mid is in the larger left run
} else {
hi = mid; // mid is in the smaller right run, maybe the minimum
}
}
return nums[lo];
}
};
Complexity Analysis
- Time: O(log n). The search range at least halves every iteration.
- Space: O(1). Only two indices.
Key Insights
- Two sorted runs: a rotated sorted array is two increasing runs, and every value in the first run is larger than every value in the second.
- Compare with the right end, not the left:
nums[hi]always belongs to the right run (or the array is unrotated), sonums[mid] > nums[hi]cleanly identifies the left run. Comparing withnums[lo]fails on an unrotated array unless you add a special case. hi = mid, nothi = mid - 1: whenmidis in the right run it might be the minimum itself, so it must stay in range.- Loop until one element remains: with
lo < hias the condition, the answer isnums[lo]when the loop ends, with no extra checks.
Edge Cases
- Single element:
[7]returns 7. The loop never runs. - Not rotated (rotated n times):
[-4, 0, 5, 8]returns -4. Every comparison moveshileft. - Two elements:
[2, 1]returns 1 and[1, 2]returns 1. - Minimum at the last index:
[3, 4, 5, 6, 1]returns 1. Every comparison movesloright. - Negative and extreme values:
[5000, -5000]returns -5000. The values never take part in arithmetic, so there is no overflow risk.
Common Mistakes
- Using
hi = mid - 1: this can skip over the minimum whenmidis the minimum. - Comparing
nums[mid]withnums[lo]:nums[mid] >= nums[lo]does not tell you which side holds the minimum when the array is not rotated. - Using
lo <= hiwithhi = mid: the loop never ends oncelo == hi. - Ignoring the distinctness assumption: with duplicates (LC 154),
nums[mid] == nums[hi]is ambiguous and needshi -= 1, which makes the worst case O(n). - Computing
midas(lo + hi) / 2in fixed-width languages: it is safe here becausen <= 5000, butlo + (hi - lo) / 2is the habit to keep.
Interview Tips
- Draw the shape: sketch the array as two rising line segments. It makes the
nums[mid]versusnums[hi]rule obvious. - State the invariant: “the minimum is always within
[lo, hi]”. It justifies both update rules and the exit condition. - Mention the linear scan first: then explain why the sorted structure allows halving the range.
- Test on the unrotated case aloud: it is the case most broken solutions get wrong.
Follow-up Questions
- What if the array can contain duplicates? That is LC 154. When
nums[mid] == nums[hi], you cannot tell which side the minimum is on, so shrink withhi -= 1. Worst case becomes O(n), for example[1, 1, 1, 0, 1]. - How many times was the array rotated? With this page’s definition (k elements moved from the front to the back), the minimum ends up at index (n - k) mod n, so k = (n - index) mod n. Under the other common convention, rotating right by k, the minimum’s index is k itself.
- Search for a target value? Find the minimum’s index first, then binary search the correct half, or use the one-pass method in Search in Rotated Sorted Array.
- Find the maximum? It sits just before the minimum:
nums[(minIndex - 1 + n) % n].