Maximum Number of Balls in a Box
EasyPrompt
You are working in a ball factory where you have n balls numbered from lowLimit up to highLimit inclusive (i.e., n == highLimit - lowLimit + 1), and an infinite number of boxes numbered from 1 to infinity.
Your job at this factory is to put each ball in the box with a number equal to the sum of digits of the ball's number. For example, the ball number 321 will be put in the box number 3 + 2 + 1 = 6 and the ball number 10 will be put in the box number 1 + 0 = 1.
Given two integers lowLimit and highLimit, return the number of balls in the box with the most balls.
Example 1:
Input: lowLimit = 1, highLimit = 10
Output: 2
Explanation:
Box Number: 1 2 3 4 5 6 7 8 9 10 11 ...
Ball Count: 2 1 1 1 1 1 1 1 1 0 0 ...
Box 1 has the most number of balls with 2 balls.Example 2:
Input: lowLimit = 5, highLimit = 15
Output: 2
Explanation:
Box Number: 1 2 3 4 5 6 7 8 9 10 11 ...
Ball Count: 1 1 1 1 2 2 1 1 1 0 0 ...
Boxes 5 and 6 have the most number of balls with 2 balls in each.Example 3:
Input: lowLimit = 19, highLimit = 28
Output: 2
Explanation:
Box Number: 1 2 3 4 5 6 7 8 9 10 11 12 ...
Ball Count: 0 1 1 1 1 1 1 1 1 2 0 0 ...
Box 10 has the most number of balls with 2 balls.
Constraints:
1 <= lowLimit <= highLimit <= 105
Approaches
3 approaches with complexity analysis and trade-offs.
This approach directly simulates the process described in the problem. We iterate through each ball number from lowLimit to highLimit. For each ball, we calculate the sum of its digits, which determines the box number. We use a HashMap to store the counts of balls in each box, where the key is the box number (digit sum) and the value is the count of balls. After processing all the balls, we find the maximum value stored in the HashMap to get the result.
Algorithm
- Initialize a
HashMap<Integer, Integer>to store the count of balls for each box number. - Loop through each ball number
ifromlowLimittohighLimit. - For each
i, calculate the sum of its digits. This sum is theboxNumber. - A helper function
getDigitSum(int n)can be used for this calculation. - Update the count for
boxNumberin the HashMap.map.put(boxNumber, map.getOrDefault(boxNumber, 0) + 1). - Keep track of the maximum count seen so far.
- After the loop, the maximum count found is the answer.
Walkthrough
This method is a straightforward translation of the problem statement into code. It's easy to understand but not the most performant due to the use of a HashMap where a simpler data structure would suffice.
- Algorithm Steps:
- Create a
HashMap<Integer, Integer>namedboxCountsto map box numbers to ball counts. - Iterate with a variable
ifromlowLimittohighLimit. - Inside the loop, for each number
i, calculate its digit sum. A helper functiongetDigitSum(int n)can be used, which repeatedly takes the number modulo 10 to get the last digit and adds it to a sum, then divides the number by 10, until the number becomes 0. - Let the calculated sum be
boxNum. Update the count for thisboxNumin theboxCountsmap usingmap.put(boxNum, map.getOrDefault(boxNum, 0) + 1). - While updating, we can also keep a running maximum to avoid a second pass over the map's values.
- Return this maximum count.
import java.util.HashMap;import java.util.Map; class Solution { public int countBalls(int lowLimit, int highLimit) { Map<Integer, Integer> boxCounts = new HashMap<>(); int maxBalls = 0; for (int i = lowLimit; i <= highLimit; i++) { int boxNum = getDigitSum(i); int currentCount = boxCounts.getOrDefault(boxNum, 0) + 1; boxCounts.put(boxNum, currentCount); if (currentCount > maxBalls) { maxBalls = currentCount; } } return maxBalls; } private int getDigitSum(int n) { int sum = 0; while (n > 0) { sum += n % 10; n /= 10; } return sum; }}Complexity
Time
O(N * log(H)), where N is the number of balls (`highLimit - lowLimit + 1`) and H is `highLimit`. The loop runs N times, and inside the loop, calculating the digit sum for a number `k` takes `O(log10(k))` time.
Space
O(K), where K is the number of unique box numbers. Given `highLimit <= 10^5`, the maximum number is `99999` and the maximum digit sum is `9*5 = 45`. Thus, K is at most 46, making the space complexity effectively O(1).
Trade-offs
Pros
Simple to understand and implement as it directly follows the problem statement.
Flexible, as it would work even if box numbers were not small, consecutive integers.
Cons
Using a
HashMapintroduces overhead from hashing, boxing/unboxing integers, and managing the map structure, making it slightly less performant in practice compared to a simple array for this problem's constraints.
This approach is an optimization over the HashMap-based solution. Since the box numbers (digit sums) are small and fall within a predictable, narrow range, we can use a fixed-size array instead of a HashMap to store the ball counts. This avoids the overhead of hashing and object creation, leading to better performance due to direct memory access.
Algorithm
- Determine the maximum possible box number. For
highLimit <= 10^5, the max sum is9*5 = 45. An array of size 46 is sufficient. - Create an integer array
boxCountsof this size, initialized to zeros. - Iterate from
lowLimittohighLimit. - For each number, calculate its digit sum (
boxNum). - Increment the count in the array:
boxCounts[boxNum]++. - After the loop, iterate through the
boxCountsarray to find the maximum value. - Return this maximum value.
Walkthrough
By analyzing the constraints, we can see that the box numbers will be small. The maximum possible highLimit is 10^5, so the largest number we might process is 99999. The sum of its digits is 9+9+9+9+9 = 45. This means all box numbers will be between 1 and 45. An array is a perfect data structure for this scenario.
- Algorithm Steps:
- Create an integer array
boxCountsof size 46 (to have indices 0 through 45), initialized to zeros. - Iterate with a variable
ifromlowLimittohighLimit. - For each number
i, calculate its digit sum, let's call itboxNum. - Increment the count for that box:
boxCounts[boxNum]++. - After the loop finishes, iterate through the
boxCountsarray to find the maximum value. - Return this maximum value.
class Solution { public int countBalls(int lowLimit, int highLimit) { // Max possible sum for a number up to 10^5 (99999) is 9*5 = 45. // Array size 46 is sufficient (indices 0 to 45). int[] boxCounts = new int[46]; for (int i = lowLimit; i <= highLimit; i++) { int boxNum = 0; int temp = i; while (temp > 0) { boxNum += temp % 10; temp /= 10; } boxCounts[boxNum]++; } int maxBalls = 0; for (int count : boxCounts) { if (count > maxBalls) { maxBalls = count; } } return maxBalls; }}Complexity
Time
O(N * log(H)), where N is `highLimit - lowLimit + 1` and H is `highLimit`. The time complexity is asymptotically the same as the HashMap approach, but with a lower constant factor, making it faster in practice.
Space
O(1), as we use a fixed-size array (size 46) regardless of the input range.
Trade-offs
Pros
More efficient than the HashMap approach due to direct array indexing and avoiding object overhead.
Constant space complexity.
Still relatively simple to implement.
Cons
Still involves re-calculating the digit sum from scratch for every number, which is not the most optimal way.
This is the most efficient approach. It builds upon the array-based simulation but optimizes the calculation of the digit sum. Instead of re-calculating the sum for each number from scratch, it computes the digit sum of a number i based on the known digit sum of i-1. This clever trick avoids the expensive repeated division and modulo operations for every number, reducing the complexity of the inner operation to amortized constant time.
Algorithm
- Create an integer array
boxCountsof size 46. - Calculate the digit sum for
lowLimitfrom scratch and store it ascurrentSum. IncrementboxCounts[currentSum]. - Iterate from
i = lowLimit + 1tohighLimit. - To get the digit sum of
i, start with the sum ofi-1. Increment it by 1. - Then, adjust for any 'carries'. For every trailing 9 in
i-1, subtract 9 from the sum. - This gives the new
currentSumfor numberi. - Increment
boxCounts[currentSum]and update the overall maximum count. - Return the maximum count after the loop.
Walkthrough
The key observation is that sum_digits(i) is closely related to sum_digits(i-1).
-
If
i-1does not end in 9, thensum_digits(i) = sum_digits(i-1) + 1. (e.g.,sum(24)=6,sum(25)=7). -
If
i-1ends in one or more 9s, a "carry" occurs. (e.g., from 29 to 30).sum(29) = 11,sum(30) = 3. The relationship issum(30) = sum(29) + 1 - 9. For each trailing 9 ini-1, we effectively subtract 9 from the sum after the initial increment. -
Algorithm Steps:
- Create an integer array
boxCountsof size 46. - Calculate the digit sum for
lowLimitfrom scratch. Let's call itcurrentSum. IncrementboxCounts[currentSum]. - Initialize a variable
maxBallsto 1. - Iterate from
i = lowLimit + 1tohighLimit. - For each
i, updatecurrentSumbased on the previous sum. Letprev = i - 1. - Increment
currentSumby 1. - While
prevends in 9 (i.e.,prev % 10 == 9), subtract 9 fromcurrentSumand divideprevby 10 to check the next digit. - Increment
boxCounts[currentSum]. - Update
maxBalls = Math.max(maxBalls, boxCounts[currentSum]). - Return
maxBalls.
class Solution { public int countBalls(int lowLimit, int highLimit) { int[] boxCounts = new int[46]; // Calculate sum for the first number int currentSum = 0; int temp = lowLimit; while (temp > 0) { currentSum += temp % 10; temp /= 10; } boxCounts[currentSum]++; int maxBalls = 1; // Iterate and calculate subsequent sums incrementally for (int i = lowLimit + 1; i <= highLimit; i++) { int prev = i - 1; currentSum++; // Increment for the last digit (e.g., from 24 to 25, sum goes from 6 to 7) // Adjust for carries (when previous number ends in 9) // e.g., from 29 to 30. sum(29)=11. currentSum becomes 12. 9 is a trailing digit. // currentSum -= 9 -> 3. which is sum(30). while (prev % 10 == 9) { currentSum -= 9; prev /= 10; } boxCounts[currentSum]++; if (boxCounts[currentSum] > maxBalls) { maxBalls = boxCounts[currentSum]; } } return maxBalls; }}Complexity
Time
O(N + log(L)), where N is `highLimit - lowLimit + 1` and L is `lowLimit`. The initial sum calculation takes `O(log(L))`. The main loop runs N times. The work inside the loop is amortized O(1) because the `while` loop's total operations over the entire run are proportional to N. This simplifies to O(N).
Space
O(1), as we use a fixed-size array.
Trade-offs
Pros
Most efficient time complexity,
O(N).Avoids redundant calculations by reusing the previous result.
Constant space complexity.
Cons
The logic for updating the sum incrementally is slightly more complex to understand and implement compared to the straightforward calculation.
Solutions
Solution
public class Solution { public int CountBalls(int lowLimit, int highLimit) { int[] cnt = new int[50]; for (int x = lowLimit; x <= highLimit; x++) { int y = 0; int n = x; while (n > 0) { y += n % 10; n /= 10; } cnt[y]++; } return cnt.Max(); }}class Solution { public int countBalls ( int lowLimit , int highLimit ) { int [] cnt = new int [ 50 ]; for ( int i = lowLimit ; i <= highLimit ; ++ i ) { int y = 0 ; for ( int x = i ; x > 0 ; x /= 10 ) { y += x % 10 ; } ++ cnt [ y ]; } return Arrays . stream ( cnt ). max (). getAsInt (); } }/** * @param {number} lowLimit * @param {number} highLimit * @return {number} */ var countBalls = function ( lowLimit , highLimit ) { const cnt = Array ( 50 ). fill ( 0 ); for ( let i = lowLimit ; i <= highLimit ; ++ i ) { let y = 0 ; for ( let x = i ; x ; x = Math . floor ( x / 10 )) { y += x % 10 ; } ++ cnt [ y ]; } return Math . max (... cnt ); };class Solution { public: int countBalls ( int lowLimit , int highLimit ) { int cnt [ 50 ] = { 0 }; int ans = 0 ; for ( int i = lowLimit ; i <= highLimit ; ++ i ) { int y = 0 ; for ( int x = i ; x ; x /= 10 ) { y += x % 10 ; } ans = max ( ans , ++ cnt [ y ]); } return ans ; } };class Solution : def countBalls ( self , lowLimit : int , highLimit : int ) -> int : cnt = [ 0 ] * 50 for x in range ( lowLimit , highLimit + 1 ): y = 0 while x : y += x % 10 x //= 10 cnt [ y ] += 1 return max ( cnt )Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.