title: Find Minimum in Rotated Sorted Array
slug: find-minimum-in-rotated-sorted-array
difficulty: medium
leetcode_id: 153
leetcode_url: https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/
categories:
  - arrays
  - binary-search
patterns:
  - binary-search

function_signature: "def find_min(nums: list[int]) -> int:"

test_cases:
  visible:
    - input: { nums: [3, 4, 5, 1, 2] }
      expected: 1
    - input: { nums: [4, 5, 6, 7, 0, 1, 2] }
      expected: 0
    - input: { nums: [11, 13, 15, 17] }
      expected: 11
  hidden:
    - input: { nums: [1] }
      expected: 1
    - input: { nums: [2, 1] }
      expected: 1
    - input: { nums: [1, 2, 3, 4, 5] }
      expected: 1
    - input: { nums: [5, 1, 2, 3, 4] }
      expected: 1

description: |
  Suppose an array of length `n` sorted in ascending order is **rotated** between `1` and `n` times. For example, the array `nums = [0,1,2,4,5,6,7]` might become:

  - `[4,5,6,7,0,1,2]` if it was rotated 4 times
  - `[0,1,2,4,5,6,7]` if it was rotated 7 times (back to original)

  Given the sorted rotated array `nums` of **unique** elements, return *the minimum element of this array*.

  You must write an algorithm that runs in **O(log n)** time.

constraints: |
  - `n == nums.length`
  - `1 <= n <= 5000`
  - `-5000 <= nums[i] <= 5000`
  - All the integers of `nums` are **unique**
  - `nums` is sorted and rotated between 1 and n times

examples:
  - input: "nums = [3,4,5,1,2]"
    output: "1"
    explanation: "Original array was [1,2,3,4,5] rotated 3 times."
  - input: "nums = [4,5,6,7,0,1,2]"
    output: "0"
    explanation: "Original array was [0,1,2,4,5,6,7] rotated 4 times."
  - input: "nums = [11,13,15,17]"
    output: "11"
    explanation: "Array was rotated 4 times (full rotation), so minimum is first element."

explanation:
  intuition: |
    Visualise a rotated sorted array: it's like taking a sorted array, cutting it somewhere, and swapping the two pieces. This creates a **pivot point** — the place where the large values suddenly drop to small values.

    For example, in `[4,5,6,7,0,1,2]`, the pivot is between 7 and 0. The minimum element is always at this pivot point!

    Think of it like this: the array has two sorted "halves". One half has larger values, the other has smaller values. The minimum is the first element of the smaller half.

    How do we find it with binary search? Compare `nums[mid]` with `nums[right]`:
    - If `nums[mid] > nums[right]`: We're in the "larger" half. The pivot (minimum) must be to the right.
    - If `nums[mid] <= nums[right]`: We're in the "smaller" half or at the minimum. The pivot is at `mid` or to the left.

    Why compare with `right` instead of `left`? Because comparing with `right` consistently tells us which "half" we're in, regardless of how much the array was rotated.

  approach: |
    We solve this using **Modified Binary Search**:

    **Step 1: Initialise pointers**

    - `left = 0`, `right = len(nums) - 1`
    - The minimum must be somewhere in `[left, right]`

    &nbsp;

    **Step 2: Binary search with right comparison**

    - While `left < right`:
      - Calculate `mid = left + (right - left) // 2`
      - If `nums[mid] > nums[right]`:
        - The pivot (minimum) is in the right half
        - Set `left = mid + 1` (exclude mid — it's too large)
      - Else (`nums[mid] <= nums[right]`):
        - The pivot is at `mid` or in the left half
        - Set `right = mid` (keep mid in consideration)

    &nbsp;

    **Step 3: Return the minimum**

    - When `left == right`, we've found the minimum
    - Return `nums[left]`

    &nbsp;

    This works because we're essentially searching for the "boundary" where the array transitions from large values to small values.

  common_pitfalls:
    - title: Comparing with Left Instead of Right
      description: |
        Comparing `nums[mid]` with `nums[left]` doesn't work consistently. Consider `[2, 1]`:
        - `mid = 0`, `nums[mid] = 2`, `nums[left] = 2`
        - `nums[mid] > nums[left]` is false, but the minimum is on the right!

        Comparing with `nums[right]` works because the right element is always either in the "smaller half" (after pivot) or the array isn't rotated.
      wrong_approach: "if nums[mid] > nums[left]: search right"
      correct_approach: "if nums[mid] > nums[right]: search right"

    - title: Using left <= right Loop Condition
      description: |
        For this problem, use `while left < right`. When `left == right`, we've found the answer. Using `<=` can cause infinite loops because we're not always excluding `mid`.
      wrong_approach: "while left <= right"
      correct_approach: "while left < right"

    - title: Excluding mid Incorrectly
      description: |
        When `nums[mid] <= nums[right]`, `mid` could be the minimum! We must keep it in consideration by setting `right = mid`, not `right = mid - 1`.

        When `nums[mid] > nums[right]`, we know `mid` is definitely not the minimum (it's larger than something to its right), so `left = mid + 1` is safe.
      wrong_approach: "right = mid - 1 when nums[mid] <= nums[right]"
      correct_approach: "right = mid (keep mid as a candidate)"

  key_takeaways:
    - "**Binary search on rotated arrays**: Compare with the right boundary to determine which half contains the answer"
    - "**Understanding the structure**: A rotated sorted array has two sorted segments — find the boundary between them"
    - "**Careful with boundary updates**: `mid + 1` vs `mid` depends on whether mid can be the answer"
    - "**Foundation for harder problems**: This technique extends to searching for any element in rotated arrays"

  time_complexity: "O(log n). Each iteration halves the search space."
  space_complexity: "O(1). Only a constant number of variables are used."

solutions:
  - approach_name: Binary Search
    is_optimal: true
    code: |
      def find_min(nums: list[int]) -> int:
          left, right = 0, len(nums) - 1

          while left < right:
              mid = left + (right - left) // 2

              if nums[mid] > nums[right]:
                  # Mid is in the "larger" half
                  # Minimum must be to the right of mid
                  left = mid + 1
              else:
                  # Mid is in the "smaller" half (or at the minimum)
                  # Minimum is at mid or to the left
                  right = mid

          # left == right, pointing to the minimum
          return nums[left]
    explanation: |
      **Time Complexity:** O(log n) — Search space halves each iteration.

      **Space Complexity:** O(1) — Constant extra space.

      We compare `nums[mid]` with `nums[right]` to determine which half contains the minimum. If `mid > right`, the pivot is on the right. Otherwise, it's at `mid` or on the left. The loop converges to the exact position of the minimum.

  - approach_name: Linear Scan
    is_optimal: false
    code: |
      def find_min(nums: list[int]) -> int:
          # Find where sorted order breaks
          for i in range(1, len(nums)):
              if nums[i] < nums[i - 1]:
                  return nums[i]

          # No break found — array wasn't rotated (or rotated fully)
          return nums[0]
    explanation: |
      **Time Complexity:** O(n) — Scans through the array.

      **Space Complexity:** O(1) — Constant extra space.

      Find the point where the sorted order breaks (current element less than previous). The element at that point is the minimum. If no break is found, the array wasn't rotated, so return the first element. This doesn't meet the O(log n) requirement but is useful for understanding the problem.
