title: Guess Number Higher or Lower
slug: guess-number-higher-or-lower
difficulty: easy
leetcode_id: 374
leetcode_url: https://leetcode.com/problems/guess-number-higher-or-lower/
categories:
  - binary-search
patterns:
  - binary-search

description: |
  We are playing the Guess Game. The game is as follows:

  I pick a number from `1` to `n`. You have to guess which number I picked (the number I picked stays the same throughout the game).

  Every time you guess wrong, I will tell you whether the number I picked is higher or lower than your guess.

  You call a pre-defined API `int guess(int num)`, which returns three possible results:

  - `-1`: Your guess is higher than the number I picked (i.e. `num > pick`).
  - `1`: Your guess is lower than the number I picked (i.e. `num < pick`).
  - `0`: Your guess is equal to the number I picked (i.e. `num == pick`).

  Return *the number that I picked*.

constraints: |
  - `1 <= n <= 2^31 - 1`
  - `1 <= pick <= n`

examples:
  - input: "n = 10, pick = 6"
    output: "6"
    explanation: "Using binary search, we narrow down the range until we find 6."
  - input: "n = 1, pick = 1"
    output: "1"
    explanation: "There's only one number, so the answer is 1."
  - input: "n = 2, pick = 1"
    output: "1"
    explanation: "We guess the middle (or lower), and the API tells us we found it or to go lower."

explanation:
  intuition: |
    Imagine playing a number guessing game with a friend. They're thinking of a number between 1 and 100, and after each guess, they tell you "higher" or "lower". What's the smartest strategy?

    The optimal approach is to **always guess the middle of the remaining range**. If they say "higher", you eliminate all numbers below your guess. If they say "lower", you eliminate all numbers above. Each guess cuts the search space in half.

    Think of it like searching for a word in a dictionary. You don't start from page 1 and check every page — you open to the middle, see if your word comes before or after, and repeat. This is the essence of **binary search**.

    The key insight is that the API feedback (`-1`, `0`, `1`) directly tells us which half of the range to keep searching. We're guaranteed to find the answer because we systematically narrow down until only one number remains.

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

    **Step 1: Initialise the search boundaries**

    - `low`: Set to `1` (the smallest possible number)
    - `high`: Set to `n` (the largest possible number)

    &nbsp;

    **Step 2: Binary search loop**

    - While `low <= high`, calculate the middle point: `mid = low + (high - low) // 2`
    - Call `guess(mid)` to get feedback
    - If result is `0`: We found the number — return `mid`
    - If result is `-1`: Our guess is too high — search in the lower half by setting `high = mid - 1`
    - If result is `1`: Our guess is too low — search in the upper half by setting `low = mid + 1`

    &nbsp;

    **Step 3: Return the result**

    - The loop is guaranteed to find the answer since `1 <= pick <= n`

    &nbsp;

    Note: We use `mid = low + (high - low) // 2` instead of `(low + high) // 2` to avoid integer overflow when `low + high` exceeds the maximum integer value.

  common_pitfalls:
    - title: Integer Overflow in Midpoint Calculation
      description: |
        A classic bug is calculating the midpoint as `(low + high) // 2`. When `low` and `high` are both large (close to `2^31 - 1`), their sum overflows.

        For example, if `low = 2^30` and `high = 2^31 - 1`, then `low + high` exceeds the 32-bit signed integer limit, causing incorrect behavior or runtime errors.

        Always use `low + (high - low) // 2` to safely compute the midpoint.
      wrong_approach: "(low + high) // 2"
      correct_approach: "low + (high - low) // 2"

    - title: Linear Search
      description: |
        Guessing numbers one by one from `1` to `n` works but is extremely inefficient. With `n` up to `2^31 - 1` (over 2 billion), a linear approach could require billions of guesses.

        Binary search guarantees finding the answer in at most `log2(n)` guesses — about 31 guesses for the maximum `n`.
      wrong_approach: "Loop from 1 to n, calling guess(i)"
      correct_approach: "Binary search halving the range each time"

    - title: Off-by-One Errors
      description: |
        When updating `low` and `high`, you must exclude the middle element since we already checked it:

        - When `guess(mid)` returns `-1` (guess too high), set `high = mid - 1`, not `high = mid`
        - When `guess(mid)` returns `1` (guess too low), set `low = mid + 1`, not `low = mid`

        Using `mid` instead of `mid - 1` or `mid + 1` can cause infinite loops.

  key_takeaways:
    - "**Binary search foundation**: This problem teaches the core binary search template — divide the search space in half based on a condition"
    - "**Overflow prevention**: Always use `low + (high - low) // 2` for midpoint calculation in production code"
    - "**Interactive problems**: Problems with API calls follow the same patterns — the API response guides which half to search"
    - "**Logarithmic efficiency**: Binary search reduces `O(n)` to `O(log n)`, essential for large input ranges"

  time_complexity: "O(log n). Each guess eliminates half of the remaining candidates, so we need at most log2(n) guesses."
  space_complexity: "O(1). We only use three variables (`low`, `high`, `mid`) regardless of the input size."

solutions:
  - approach_name: Binary Search
    is_optimal: true
    code: |
      # The guess API is already defined for you.
      # @param num: your guess
      # @return -1 if num is higher than the picked number
      #          1 if num is lower than the picked number
      #          otherwise return 0
      # def guess(num: int) -> int:

      def guess_number(n: int) -> int:
          low, high = 1, n

          while low <= high:
              # Safe midpoint calculation to avoid overflow
              mid = low + (high - low) // 2

              result = guess(mid)

              if result == 0:
                  # Found the number
                  return mid
              elif result == -1:
                  # Guess is too high, search lower half
                  high = mid - 1
              else:
                  # Guess is too low, search upper half
                  low = mid + 1

          # Should never reach here given problem constraints
          return -1
    explanation: |
      **Time Complexity:** O(log n) — Each iteration halves the search space.

      **Space Complexity:** O(1) — Only three integer variables used.

      This is the classic binary search template. We maintain a range `[low, high]` and repeatedly guess the middle value. Based on the API response, we eliminate half the range until we find the target.

  - approach_name: Linear Search
    is_optimal: false
    code: |
      def guess_number(n: int) -> int:
          # Check each number from 1 to n
          for i in range(1, n + 1):
              if guess(i) == 0:
                  return i

          return -1
    explanation: |
      **Time Complexity:** O(n) — In the worst case, we check every number.

      **Space Complexity:** O(1) — Only loop variable used.

      This brute force approach checks numbers sequentially. While correct, it's far too slow for large `n` (up to 2 billion). With `n = 2^31 - 1`, this could require over 2 billion API calls, causing Time Limit Exceeded. Included to illustrate why binary search is necessary.
