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.
Return the number of complete rows of the staircase you will build.
Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Binary Search - Searching for the optimal value in a sorted search space
Arithmetic Series - The formula for sum of first k integers: k * (k + 1) / 2
Basic Math - Solving quadratic inequalities and using the quadratic formula
1. Brute Force
Intuition
We are building a staircase where row 1 needs 1 coin, row 2 needs 2 coins, and so on. The question is: how many complete rows can we build with n coins?
The simplest approach is to simulate the process. Keep adding rows one by one, subtracting the required coins from n until we cannot complete another row.
Algorithm
Initialize row = 0.
While we have enough coins for the next row (n > row):
class Solution {funarrangeCoins(n: Int): Int {var n = n
var row =0while(n - row >0){
row++
n -= row
}return row
}}
classSolution{funcarrangeCoins(_ n:Int)->Int{var n = n
var row =0while n - row >0{
row +=1
n -= row
}return row
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
2. Binary Search
Intuition
Building rows one by one is slow for large n. Instead, we can use the formula for the sum of first k integers: k * (k + 1) / 2. If this sum is at most n, we can build k complete rows.
This creates a monotonic condition perfect for binary search. We search for the largest k such that k * (k + 1) / 2 <= n.
Algorithm
Set search bounds: l = 1 and r = n.
While l <= r:
Compute mid and calculate coins needed for mid rows.
If coins needed exceeds n, search the left half.
Otherwise, update the result and search the right half.
classSolution:defarrangeCoins(self, n:int)->int:
l, r =1, n
res =0while l <= r:
mid =(l + r)//2
coins =(mid *(mid +1))//2if coins > n:
r = mid -1else:
l = mid +1
res =max(res, mid)return res
publicclassSolution{publicintarrangeCoins(int n){int l =1, r = n, res =0;while(l <= r){int mid = l +(r - l)/2;long coins =(long) mid *(mid +1)/2;if(coins > n){
r = mid -1;}else{
l = mid +1;
res =Math.max(res, mid);}}return res;}}
classSolution{public:intarrangeCoins(int n){longlong l =1, r = n, res =0;while(l <= r){longlong mid = l +(r - l)/2;longlong coins =(mid *(mid +1))/2;if(coins > n){
r = mid -1;}else{
l = mid +1;
res =max(res, mid);}}return res;}};
classSolution{/**
* @param {number} n
* @return {number}
*/arrangeCoins(n){let l =1,
r = n,
res =0;while(l <= r){let mid = Math.floor((l + r)/2);let coins =(mid *(mid +1))/2;if(coins > n){
r = mid -1;}else{
l = mid +1;
res = Math.max(res, mid);}}return res;}}
publicclassSolution{publicintArrangeCoins(int n){int l =1, r = n;int res =0;while(l <= r){int mid = l +(r - l)/2;long coins =(long)mid *(mid +1)/2;if(coins > n){
r = mid -1;}else{
res = Math.Max(res, mid);
l = mid +1;}}return res;}}
funcarrangeCoins(n int)int{
l, r :=1, n
res :=0for l <= r {
mid := l +(r-l)/2
coins :=int64(mid)*int64(mid+1)/2if coins >int64(n){
r = mid -1}else{
l = mid +1if mid > res {
res = mid
}}}return res
}
class Solution {funarrangeCoins(n: Int): Int {var l =1var r = n
var res =0while(l <= r){val mid = l +(r - l)/2val coins = mid.toLong()*(mid +1)/2if(coins > n){
r = mid -1}else{
l = mid +1
res =maxOf(res, mid)}}return res
}}
classSolution{funcarrangeCoins(_ n:Int)->Int{var l =1var r = n
var res =0while l <= r {let mid = l +(r - l)/2let coins = mid *(mid +1)/2if coins > n {
r = mid -1}else{
l = mid +1
res =max(res, mid)}}return res
}}
Time & Space Complexity
Time complexity: O(logn)
Space complexity: O(1)
3. Binary Search (Optimal)
Intuition
We can optimize the binary search by tightening the upper bound. Since k * (k + 1) / 2 <= n, we know k is roughly sqrt(2n). A safe upper bound is n / 2 + 1 for n > 3, which reduces the search space.
We also simplify the binary search by finding the first k where the condition fails, then returning k - 1.
Algorithm
Handle small cases: if n <= 3, return 1 for n = 1 and n - 1 otherwise.
Set l = 1 and r = n / 2 + 1.
Use binary search to find the smallest k where coins exceed n.
classSolution:defarrangeCoins(self, n:int)->int:if n <=3:return n if n ==1else n -1
l, r =1,(n //2)+1while l < r:
mid =(l + r)//2if(mid *(mid +1))//2<= n:
l = mid +1else:
r = mid
return l -1
publicclassSolution{publicintarrangeCoins(int n){if(n <=3){return n ==1?1: n -1;}int l =1, r =(n /2)+1;while(l < r){int mid = l +(r - l)/2;long coins =(long) mid *(mid +1)/2;if(coins <= n){
l = mid +1;}else{
r = mid;}}return l -1;}}
classSolution{public:intarrangeCoins(int n){if(n <=3){return n ==1?1: n -1;}int l =1, r =(n /2)+1;while(l < r){int mid = l +(r - l)/2;longlong coins =(mid *(mid +1LL))/2;if(coins <= n){
l = mid +1;}else{
r = mid;}}return l -1;}};
classSolution{/**
* @param {number} n
* @return {number}
*/arrangeCoins(n){if(n <=3){return n ==1?1: n -1;}let l =1,
r = n /2+1;while(l < r){let mid = Math.floor((l + r)/2);let coins =(mid *(mid +1))/2;if(coins <= n){
l = mid +1;}else{
r = mid;}}return l -1;}}
publicclassSolution{publicintArrangeCoins(int n){if(n <=3){return n ==1?1: n -1;}int l =1, r =(n /2)+1;while(l < r){int mid =(l + r)/2;long coins =(long)mid *(mid +1)/2;if(coins <= n){
l = mid +1;}else{
r = mid;}}return l -1;}}
funcarrangeCoins(n int)int{if n <=3{if n ==1{return1}return n -1}
l, r :=1, n/2+1for l < r {
mid :=(l + r)/2
coins :=int64(mid)*int64(mid+1)/2if coins <=int64(n){
l = mid +1}else{
r = mid
}}return l -1}
class Solution {funarrangeCoins(n: Int): Int {if(n <=3){returnif(n ==1)1else n -1}var l =1var r = n /2+1while(l < r){val mid =(l + r)/2val coins = mid.toLong()*(mid +1)/2if(coins <= n){
l = mid +1}else{
r = mid
}}return l -1}}
classSolution{funcarrangeCoins(_ n:Int)->Int{if n <=3{return n ==1?1: n -1}var l =1var r = n /2+1while l < r {let mid =(l + r)/2let coins = mid *(mid +1)/2if coins <= n {
l = mid +1}else{
r = mid
}}return l -1}}
Time & Space Complexity
Time complexity: O(logn)
Space complexity: O(1)
4. Bit Manipulation
Intuition
We can build the answer bit by bit, from the most significant bit down. For each bit position, we tentatively set it and check if the resulting number of rows is valid. If valid, we keep the bit; otherwise, we clear it.
Since n fits in 32 bits and k is roughly sqrt(n), we only need about 16 bits to represent the answer.
Algorithm
Start with a mask at the highest relevant bit (bit 15).
We can solve this directly with algebra. We need the largest k where k * (k + 1) / 2 <= n. Rearranging: k^2 + k - 2n <= 0. Using the quadratic formula, k = (-1 + sqrt(1 + 8n)) / 2.
Simplifying gives us k = sqrt(2n + 0.25) - 0.5. We take the floor of this value to get the answer.
Time complexity: O(1) or O(n) depending on the language.
Space complexity: O(1)
Common Pitfalls
Integer Overflow When Computing mid * (mid + 1)
When using binary search, computing mid * (mid + 1) can overflow for large values of mid if using 32-bit integers. Always cast to a larger type before multiplication.
// Wrong: overflow when mid is largeint coins = mid *(mid +1)/2;// Correct: cast to long firstlong coins =(long) mid *(mid +1)/2;
Off-by-One Error in the Result
The formula k * (k + 1) / 2 gives the total coins needed for k complete rows. A common mistake is returning the wrong value when the sum exactly equals n or when determining whether to include the current row.
# Wrong: returning l instead of l - 1 after binary searchwhile l < r:
mid =(l + r)//2if mid *(mid +1)//2<= n:
l = mid +1else:
r = mid
return l # Should be l - 1