Problem overview
The method sumOfSquares receives a non-negative integer n and returns 1^2 + 2^2 + 3^2 + ... + n^2. For n = 3 the terms are 1, 4 and 9, so the answer is 14. For n = 0 there are no terms to add, and the sum is 0.
Approach: keep a running total
The pattern is the same as adding a column of numbers on paper. Start with a total of 0, visit each number from 1 to n once, square it, and add the square to the total. When the last number has been added, the total is the answer.
Three pieces are needed: a variable that lives for the whole method (the total), a loop that produces the numbers 1 to n, and one line in the loop body that updates the total. Because the loop runs n times and each pass does a fixed amount of work, the method takes time proportional to n.
Step by step
- Declare the total. It is called
sum, it has the typenumber, and it starts at 0 because nothing has been added yet. It is declared withlet, notconst, since the loop changes its value. - Write the loop header. The index
istarts at 1, the first number to square. The condition isi <= n, with<=so thatnitself is included; with<the last square would be missing.i++moves to the next number after each pass. - Add the square. Inside the loop,
sum += i * isquares the current number and adds it to the total. The operator+=is shorthand forsum = sum + i * i. - Return the total. The
returngoes after the loop, once every square has been added. Putting it inside the loop would end the method during the first pass.
The solution in TypeScript
class Solution {
sumOfSquares(n: number): number {
let sum: number = 0;
for (let i = 1; i <= n; i++) {
sum += i * i;
}
return sum;
}
}
A few TypeScript details are worth knowing:
- Annotations are for the compiler only. The
: numberinlet sum: number = 0and the: numberreturn type are checked when the code is compiled, then removed from the JavaScript that runs. TypeScript would infer the type ofsumfrom the0anyway, so the annotation here is optional. let ibelongs to the loop. A variable declared in the loop header exists only inside the loop, so a lateriin the same method would be a separate variable.- There is no
inttype. Every number is a double-precision float. Squares of whole numbers are exact as long as the values stay below 2^53, about 9 quadrillion. i * iori ** 2. TypeScript has an exponent operator,**, but multiplying a number by itself is clear and needs no explanation.
Complexity
Time: O(n). The loop body runs exactly n times, and each pass is a multiplication and an addition.
Space: O(1). The method uses only sum and i, no matter how large n is.
A closed formula, n(n + 1)(2n + 1) / 6, would give the answer without a loop. The loop is the point of this lesson, and it is the pattern you will reuse whenever a result is built from many small pieces.
Edge cases and common mistakes
n = 0. The condition1 <= 0is false at once, the loop body never runs, and the method returns the starting value0. That is the correct answer.n = 1. The loop runs once and the result is1.- Starting at 0. It is harmless here because 0 squared is 0, but it does one pointless pass.
- Using
<instead of<=. Forn = 3the method would return 5 (1 + 4) instead of 14. - Adding
iinstead ofi * i. That computes 1 + 2 + ... + n, which is 6 forn = 3. - Resetting the total inside the loop. If
let sum = 0sat inside the loop, each pass would start from zero and the method would return only the last square. - Very large
n. Past 2^53 anumbercan no longer hold every integer. ComputingsumOfSquares(1000000)with this loop gives333333833333127550, while the exact value is333333833333500000. For inputs of ordinary size, such asn = 300000, whose answer is9000045000050000, the result is exact.
Testing
sumOfSquares(3)gives14.sumOfSquares(0)gives0.sumOfSquares(1)gives1.sumOfSquares(10)gives385.