Problem overview
The method findLargestNumber(S: Int) returns the largest n for which 1² + 2² + 3² + ... + n² is at most S. The sum of squares grows with every term, so the answer is the point where adding one more square would go over the limit.
For S = 35 the running sums are 1, 5, 14 and 30. Adding 5² = 25 would give 55, which is more than 35, so the answer is 4.
Approach
Add the squares one at a time and stop before the sum exceeds S. Keep two variables: totalSoFar holds the sum of the squares added so far, and number is the next base to square. The loop condition looks one square ahead: totalSoFar + number * number <= S. When it fails, adding number would break the limit, so the answer is the number before it.
No formula is needed. The sum of the first n squares does have a closed form, but the loop is simple, and since the sum grows roughly like n³ / 3, it stops after only a few passes even for large S.
Step by step
- Start the sum at 0.
var totalSoFar = 0holds the squares added so far. Nothing has been added yet. - Start counting at 1.
var number = 1is the base of the next square, because the sum begins with 1². - Write the loop condition.
while (totalSoFar + number * number <= S)asks whether the next square still fits. Checking before adding meanstotalSoFarnever goes aboveS. - Add the square.
totalSoFar += number * numberincludes this term in the running sum. - Move to the next number.
number += 1is required: without it the loop never ends. Scala has no++operator, so use+= 1. - Return
number - 1. After the last successful pass,numberwas incremented once more, so it sits one past the last number that fit.
With S = 35, the loop adds 1, 4, 9 and 16, reaching a total of 30 with number equal to 5. Then 30 + 25 = 55 is more than 35, the loop stops, and the method returns 4.
The solution in Scala
class Solution {
def findLargestNumber(S: Int): Int = {
var totalSoFar = 0
var number = 1
while (totalSoFar + number * number <= S) {
totalSoFar += number * number
number += 1
}
return number - 1
}
}
Some Scala details:
- Two
vars. Both values change inside the loop, so neither can be aval. Scala infersIntfor both from the literals. - Compound assignment.
totalSoFar += number * numbermultiplies first and then adds, because*binds tighter than+=. - Parameter names. The parameter is the capital
S, as in the method signature. Scala is case-sensitive, soswould be a different name. - Optional
return. The last expression of a block is its value, so ending withnumber - 1alone would work as well. The explicitreturnis allowed here, because it is not inside a lambda.
Complexity
Time: O(S^(1/3)). The loop stops when the sum of the first n squares passes S. That sum is about n³ / 3, so n is about the cube root of 3S. Each pass is constant work. In terms of the answer n, the time is O(n).
Space: O(1). Two integers are all that is stored.
Edge cases and common mistakes
- Exact hit. When the sum equals
S, the condition uses<=, so that term is included. ForS = 30the answer is 4, since 1 + 4 + 9 + 16 is exactly 30. - Small
S. ForS = 1the answer is 1. ForS = 4it is still 1, because 1 + 4 = 5 is above 4. ForS = 5it is 2. - Checking after adding. If the loop adds first and tests the sum afterwards, the sum overshoots
Sbefore the loop notices, and the result is off by one. - Forgetting the
- 1. Returningnumbergives 5 forS = 35. - Overflow.
Intis 32 bits and overflow wraps silently. For values ofSnearInt.MaxValue, the sum ornumber * numbercan wrap to a negative value that passes the test. UseLongifScan be that large.
Testing
S = 35gives 4.S = 1gives 1.S = 4gives 1.S = 5gives 2.S = 55gives 5, because 1 + 4 + 9 + 16 + 25 = 55.S = 100gives 6.
