questions A (01-matrix - avoid-flood)

This commit is contained in:
2025-05-24 21:40:39 +01:00
parent 09ec96a282
commit 0b83eff6f8
55 changed files with 10813 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
title: Arranging Coins
slug: arranging-coins
difficulty: easy
leetcode_id: 441
leetcode_url: https://leetcode.com/problems/arranging-coins/
categories:
- math
- binary-search
patterns:
- binary-search
description: |
You have `n` coins and you want to build a staircase with these coins. The staircase consists of `k` rows where the i<sup>th</sup> row has exactly `i` coins. The last row of the staircase **may be** incomplete.
Given the integer `n`, return *the number of **complete rows** of the staircase you will build*.
constraints: |
- `1 <= n <= 2^31 - 1`
examples:
- input: "n = 5"
output: "2"
explanation: "Because the 3rd row is incomplete, we return 2."
- input: "n = 8"
output: "3"
explanation: "Because the 4th row is incomplete, we return 3."
explanation:
intuition: |
Imagine stacking coins row by row: the 1<sup>st</sup> row needs 1 coin, the 2<sup>nd</sup> row needs 2 coins, the 3<sup>rd</sup> row needs 3 coins, and so on.
The total number of coins needed for `k` complete rows is `1 + 2 + 3 + ... + k`, which equals `k(k+1)/2` (the triangular number formula).
Think of it like this: we're searching for the **largest value of k** such that the sum of the first k natural numbers does not exceed n. This is essentially asking: "What's the biggest staircase I can fully build with n coins?"
Since the sum `k(k+1)/2` increases monotonically with k, we can use **binary search** to efficiently find the answer. Alternatively, we can solve the quadratic equation directly using the quadratic formula for an O(1) mathematical solution.
approach: |
We can solve this using **Binary Search** on the number of rows:
**Step 1: Define the search space**
- `left`: Set to `1` (minimum possible complete rows)
- `right`: Set to `n` (maximum possible rows, though we'll never need this many)
&nbsp;
**Step 2: Binary search for the largest valid k**
- Calculate `mid` as the midpoint of the current range
- Compute the coins needed for `mid` complete rows: `mid * (mid + 1) / 2`
- If coins needed equals `n`, we found an exact match - return `mid`
- If coins needed is less than `n`, we might be able to fit more rows - search right
- If coins needed is greater than `n`, we need fewer rows - search left
&nbsp;
**Step 3: Return the result**
- When the loop ends, `right` contains the largest k where `k(k+1)/2 <= n`
- Return `right` as the number of complete rows
&nbsp;
The binary search efficiently narrows down the answer in O(log n) time instead of simulating the stacking process.
common_pitfalls:
- title: The Simulation Trap
description: |
A naive approach is to simulate building the staircase row by row:
- Start with row 1, subtract 1 coin
- Move to row 2, subtract 2 coins
- Continue until you can't complete a row
While correct, this takes **O(sqrt(n)) time** because you'll build approximately sqrt(2n) rows. For `n = 2^31 - 1`, that's about 65,000 iterations. Binary search does it in about 31 iterations.
wrong_approach: "Simulate stacking row by row"
correct_approach: "Binary search on the number of rows"
- title: Integer Overflow
description: |
When computing `mid * (mid + 1) / 2`, the multiplication can overflow if `mid` is large. With `n` up to `2^31 - 1`, `mid` could be around `65,000`, and `mid * (mid + 1)` could exceed 32-bit integer limits.
In Python this isn't an issue due to arbitrary precision integers, but in languages like Java or C++, you need to use `long` or divide before multiplying: `mid / 2 * (mid + 1)` or `mid * (mid + 1) / 2` with long types.
wrong_approach: "Use 32-bit integers without overflow protection"
correct_approach: "Use 64-bit integers or Python's arbitrary precision"
- title: Off-by-One Errors
description: |
Binary search boundary conditions can be tricky. A common mistake is returning `left` instead of `right`, or using the wrong comparison operator.
The key insight: we want the **largest** k where `k(k+1)/2 <= n`. When the search converges, `right` holds this value because we move `right = mid - 1` when we have too many coins.
wrong_approach: "Incorrect boundary handling in binary search"
correct_approach: "Carefully track which boundary holds the answer"
key_takeaways:
- "**Triangular number formula**: The sum `1 + 2 + ... + k = k(k+1)/2` appears frequently in problems involving sequential additions"
- "**Binary search on answer**: When searching for the largest/smallest value satisfying a condition, binary search is often applicable"
- "**Mathematical solutions**: Some binary search problems have closed-form solutions using algebra (quadratic formula here)"
- "**Monotonic property**: Binary search works because `k(k+1)/2` strictly increases with k - if k rows need more than n coins, so will k+1 rows"
time_complexity: "O(log n). Binary search halves the search space each iteration, and the search space is at most n."
space_complexity: "O(1). We only use a constant number of variables (`left`, `right`, `mid`, `coins_needed`)."
solutions:
- approach_name: Binary Search
is_optimal: true
code: |
def arrange_coins(n: int) -> int:
left, right = 1, n
while left <= right:
mid = left + (right - left) // 2
# Coins needed for mid complete rows
coins_needed = mid * (mid + 1) // 2
if coins_needed == n:
# Exact fit - mid rows use exactly n coins
return mid
elif coins_needed < n:
# We have coins left over, try more rows
left = mid + 1
else:
# Not enough coins, try fewer rows
right = mid - 1
# right is the largest k where k(k+1)/2 <= n
return right
explanation: |
**Time Complexity:** O(log n) - Binary search on the range [1, n].
**Space Complexity:** O(1) - Only constant extra space used.
We binary search for the largest k such that k(k+1)/2 <= n. Each iteration halves the search space, and we use the triangular number formula to compute coins needed in O(1).
- approach_name: Mathematical (Quadratic Formula)
is_optimal: true
code: |
import math
def arrange_coins(n: int) -> int:
# We need the largest k where k(k+1)/2 <= n
# Solving k^2 + k - 2n = 0 using quadratic formula:
# k = (-1 + sqrt(1 + 8n)) / 2
return int((-1 + math.sqrt(1 + 8 * n)) / 2)
explanation: |
**Time Complexity:** O(1) - Direct calculation using the quadratic formula.
**Space Complexity:** O(1) - Only constant extra space used.
We solve k(k+1)/2 <= n algebraically. Rearranging to k^2 + k - 2n <= 0 and applying the quadratic formula gives k = (-1 + sqrt(1 + 8n)) / 2. Taking the floor gives the largest valid k.
- approach_name: Linear Simulation
is_optimal: false
code: |
def arrange_coins(n: int) -> int:
rows = 0
coins_remaining = n
# Build rows one by one until we can't complete one
while coins_remaining >= rows + 1:
rows += 1
coins_remaining -= rows
return rows
explanation: |
**Time Complexity:** O(sqrt(n)) - We build approximately sqrt(2n) rows.
**Space Complexity:** O(1) - Only constant extra space used.
This simulates the stacking process directly. While intuitive and correct, it's slower than the binary search or mathematical approaches. Included to show the progression from brute force to optimal solution.