questions F-L
This commit is contained in:
170
backend/data/questions/guess-number-higher-or-lower.yaml
Normal file
170
backend/data/questions/guess-number-higher-or-lower.yaml
Normal file
@@ -0,0 +1,170 @@
|
||||
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)
|
||||
|
||||
|
||||
|
||||
**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`
|
||||
|
||||
|
||||
|
||||
**Step 3: Return the result**
|
||||
|
||||
- The loop is guaranteed to find the answer since `1 <= pick <= n`
|
||||
|
||||
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user