Skip to content

153. Find Minimum in Rotated Sorted Array (Medium)

Problem

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. Given the rotated array (with unique elements), return the minimum element. The algorithm must run in O(logn)O(log n).

Example

  • nums = [3,4,5,1,2]1
  • nums = [4,5,6,7,0,1,2]0
  • nums = [11,13,15,17]11 (not rotated, or rotated by n)

LeetCode 153 · Link · Medium

Try it yourself

idle

Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.

Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 1: Brute force, min(nums)

def find_min(nums: list[int]) -> int:
return min(nums) # L1: O(n) linear scan to find minimum

Where the time goes, line by line

Variables: n = len(nums).

LinePer-call costTimes executedContribution
L1 (min scan)O(n)O(n)1O(n)O(n) ← dominates

Python’s min() / JS’s Math.min() scans all n elements unconditionally.

Complexity

  • Time: O(n)O(n), driven by L1 (linear scan ignoring the rotated-sorted structure).
  • Space: O(1)O(1).

Correct but violates the O(logn)O(log n) constraint.

Approach 2: Walk until the first decrease

If the array is rotated, the minimum is at the first drop. Scan linearly.

def find_min(nums: list[int]) -> int:
for i in range(1, len(nums)): # L1: scan, up to n-1 steps
if nums[i] < nums[i - 1]: # L2: O(1) drop check
return nums[i]
return nums[0] # not rotated (or rotated by n), first element is minimum

Where the time goes, line by line

Variables: n = len(nums).

LinePer-call costTimes executedContribution
L1/L2 (scan for drop)O(1)O(1)up to n-1O(n)O(n) ← dominates

Stops at the first drop, so best case is O(1)O(1) (minimum at index 1), worst case O(n)O(n) (not rotated, must scan the whole array).

Complexity

  • Time: O(n)O(n), driven by L1 (linear scan for the rotation point).
  • Space: O(1)O(1).

Same Big-O as Approach 1 but makes the structural observation that drives the binary search.

Approach 3: Binary search on the rotation pivot (optimal)

Compare nums[mid] to nums[hi]:

  • If nums[mid] > nums[hi] → the min is in the right half; move lo = mid + 1.
  • Otherwise → the min is in the left half including mid; move hi = mid.

When lo == hi, you’re at the minimum.

def find_min(nums: list[int]) -> int:
lo, hi = 0, len(nums) - 1 # L1: O(1)
while lo < hi: # L2: loop, O(log n) iterations
mid = (lo + hi) // 2 # L3: O(1) midpoint
if nums[mid] > nums[hi]: # L4: O(1) compare to right boundary
lo = mid + 1 # L5: O(1) min is in right half
else:
hi = mid # L6: O(1) min is mid or left of mid
return nums[lo]

Where the time goes, line by line

Variables: n = len(nums).

LinePer-call costTimes executedContribution
L1 (init)O(1)O(1)1O(1)O(1)
L2/L3/L4 (loop body)O(1)O(1)log nO(logn)O(log n) ← dominates
L5 or L6 (narrow)O(1)O(1)log nO(logn)O(log n)

Each iteration eliminates half the range. Unlike standard binary search, the loop condition is lo < hi (not lo <= hi) because we keep hi as a possible answer when the min could be at mid.

Complexity

  • Time: O(logn)O(log n), driven by L2 (loop halves the search space each step).
  • Space: O(1)O(1).

Why compare to hi, not lo?

Comparing nums[mid] to nums[lo] is subtler because a not-rotated segment [lo, mid] can look identical to a rotated one starting past mid. Comparing to nums[hi] uniquely identifies which half contains the minimum.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Swift notes

Swift’s min() returns an optional because an array can be empty at the type level, even though this problem promises a nonempty input. The brute-force approach unwraps that result with an explicit precondition failure. The binary-search approach relies on the stronger problem contract and indexes directly.

Summary

ApproachTimeSpace
min(nums)O(n)O(n)O(1)O(1)
Linear until decreaseO(n)O(n)O(1)O(1)
Binary searchO(logn)O(log n)O(1)O(1)

The “compare to nums[hi]” trick is the key insight; it transfers directly to problem 33 (Search in Rotated Sorted Array).

Test cases

# Quick smoke tests - paste into a REPL or save as test_153.py and run.
# Uses the optimal Approach 3 implementation.
def find_min(nums: list) -> int:
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] > nums[hi]:
lo = mid + 1
else:
hi = mid
return nums[lo]
def _run_tests():
assert find_min([3, 4, 5, 1, 2]) == 1
assert find_min([4, 5, 6, 7, 0, 1, 2]) == 0
assert find_min([11, 13, 15, 17]) == 11 # not rotated
assert find_min([1]) == 1 # single element
assert find_min([2, 1]) == 1 # two elements, rotated
assert find_min([1, 2]) == 1 # two elements, not rotated
print("all tests pass")
if __name__ == "__main__":
_run_tests()
  • Arrays, rotated-sorted invariant; binary search on the pivot
  • Modified Binary Search, the binary search variant for rotated, peaked, or partly ordered data.
  • Binary Search, the halving tactic for ordered spaces where one side can be discarded.