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 function_signature: "def arrange_coins(n: int) -> int:" test_cases: visible: - input: { n: 5 } expected: 2 - input: { n: 8 } expected: 3 hidden: - input: { n: 1 } expected: 1 - input: { n: 3 } expected: 2 - input: { n: 6 } expected: 3 - input: { n: 10 } expected: 4 - input: { n: 100 } expected: 13 description: | You have `n` coins and you want to build a staircase with these coins. The staircase consists of `k` rows where the ith 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 1st row needs 1 coin, the 2nd row needs 2 coins, the 3rd 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)   **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   **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   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.