LeetCode 1742 - Maximum Number of Balls in a Box
The problem describes a scenario in which you have a sequence of balls numbered consecutively from lowLimit to highLimit, inclusive. Each ball must be placed into a box, where the box number is determined by the sum of the digits of the ball’s number.
Difficulty: 🟢 Easy
Topics: Hash Table, Math, Counting
Solution
Problem Understanding
The problem describes a scenario in which you have a sequence of balls numbered consecutively from lowLimit to highLimit, inclusive. Each ball must be placed into a box, where the box number is determined by the sum of the digits of the ball’s number. For example, ball 321 goes into box 3 + 2 + 1 = 6. The task is to determine which box ends up containing the most balls and return the number of balls in that box.
The input consists of two integers, lowLimit and highLimit. The difference between them, plus one, gives the total number of balls. The expected output is a single integer representing the maximum number of balls found in any box after all balls have been placed.
The constraints indicate that the numbers are relatively small (1 <= lowLimit <= highLimit <= 10^5), so a brute-force solution that iterates through every ball and calculates the digit sum is feasible. The problem also guarantees that lowLimit and highLimit are positive and well-ordered, so we do not need to handle invalid ranges.
Edge cases include when lowLimit == highLimit (only one ball exists), or when all numbers fall into the same box due to low digit sums. Another subtle case is when the sums of digits increase slowly, causing some boxes to remain empty.
Approaches
The brute-force approach is straightforward: iterate through each number from lowLimit to highLimit, compute the sum of its digits, and increment a counter for that box in a hash map or array. After processing all balls, simply find the maximum count among all boxes. This approach is correct because each ball is handled independently, but computing the sum of digits for every number repeatedly may be slightly inefficient.
The key optimization arises from recognizing that the sum of digits of consecutive numbers changes predictably. When moving from x to x + 1, the sum of digits increases by 1 unless there is a carry (e.g., from 19 to 20). While this observation could allow a digit-sum “incremental update,” the constraints are small enough that the brute-force approach with a simple hash map is already efficient and easy to implement.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * d) | O(k) | n = highLimit - lowLimit + 1, d = number of digits, k = max possible box number |
| Optimal | O(n * d) | O(k) | Same as brute force for this input range; uses a hash map to track counts |
Algorithm Walkthrough
- Initialize a hash map (or dictionary) to store counts of balls per box. The keys represent box numbers (sum of digits) and the values represent the count of balls in each box.
- Iterate over all numbers from
lowLimittohighLimit. - For each number, calculate the sum of its digits. This sum is the box number.
- Increment the count for this box in the hash map.
- After all numbers have been processed, find the maximum value in the hash map.
- Return this maximum value, which represents the number of balls in the box with the most balls.
Why it works: Each ball is placed into a box exactly according to the sum of its digits. Counting each placement guarantees that the box with the maximum count correctly reflects the box with the most balls.
Python Solution
class Solution:
def countBalls(self, lowLimit: int, highLimit: int) -> int:
from collections import defaultdict
box_counts = defaultdict(int)
for number in range(lowLimit, highLimit + 1):
box_number = sum(int(digit) for digit in str(number))
box_counts[box_number] += 1
return max(box_counts.values())
In this implementation, we use a defaultdict to automatically handle boxes that have not yet been encountered. For each number, we convert it to a string and sum its digits to determine the box number, then increment the count. Finally, max returns the highest ball count.
Go Solution
func countBalls(lowLimit int, highLimit int) int {
boxCounts := make(map[int]int)
maxCount := 0
for num := lowLimit; num <= highLimit; num++ {
n := num
sum := 0
for n > 0 {
sum += n % 10
n /= 10
}
boxCounts[sum]++
if boxCounts[sum] > maxCount {
maxCount = boxCounts[sum]
}
}
return maxCount
}
In Go, we use a map[int]int for counting. The digit sum is computed using modulo and division to avoid string conversion. We also maintain a running maxCount to avoid iterating over the map at the end.
Worked Examples
Example 1: lowLimit = 1, highLimit = 10
| Number | Digit Sum (Box) | Count Update | Box Counts |
|---|---|---|---|
| 1 | 1 | +1 | {1:1} |
| 2 | 2 | +1 | {1:1, 2:1} |
| 3 | 3 | +1 | {1:1,2:1,3:1} |
| 4 | 4 | +1 | {1:1,2:1,3:1,4:1} |
| 5 | 5 | +1 | {1:1,2:1,3:1,4:1,5:1} |
| 6 | 6 | +1 | {1:1,...,6:1} |
| 7 | 7 | +1 | ... |
| 8 | 8 | +1 | ... |
| 9 | 9 | +1 | ... |
| 10 | 1 | +1 | {1:2,...} |
Maximum count = 2 for box 1.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * d) | n = highLimit - lowLimit + 1, d = number of digits per number; summing digits requires O(d) |
| Space | O(k) | k = maximum possible box number (digit sum), store count for each box |
The solution scales linearly with the number of balls, and space scales with the number of distinct digit sums, which is at most 45 for numbers <= 99999.
Test Cases
# Basic examples
assert Solution().countBalls(1, 10) == 2 # Box 1 has 2 balls
assert Solution().countBalls(5, 15) == 2 # Boxes 5 and 6 have 2 balls
assert Solution().countBalls(19, 28) == 2 # Box 10 has 2 balls
# Edge cases
assert Solution().countBalls(1, 1) == 1 # Single ball, single box
assert Solution().countBalls(10, 10) == 1 # Single ball with sum 1
assert Solution().countBalls(1, 100000) >= 1 # Large range, ensure no crash
| Test | Why |
|---|---|
| 1 to 10 | Simple incremental test with overlap in box 1 |
| 5 to 15 | Overlapping boxes, verifying counts across ranges |
| 19 to 28 | Boxes with two-digit sums |
| 1 to 1 | Minimal input, single ball |
| 10 to 10 | Minimal input with sum > 1 |
| 1 to 100000 | Stress test for large inputs |
Edge Cases
First, the case where lowLimit equals highLimit tests the algorithm's ability to handle a single ball. The implementation correctly counts it in the appropriate box and returns 1.
Second, ranges where all numbers have the same digit sum could potentially stress the counting logic. The algorithm uses a hash map, so all balls accumulate correctly.
Third, very large ranges, near the upper constraint of 10^5, test performance. The algorithm remains efficient because iterating 10^5 numbers with small digit sums is computationally feasible. Additionally, Go’s integer arithmetic prevents overflow issues.