Problem overview
The function count_pairs(n: i32, sum: i32) -> i32 receives two positive integers. It must count the pairs (x, y) with 1 <= x, y <= n and x + y == sum. The order inside a pair does not matter: (1, 5) and (5, 1) are the same pair, so it is counted once.
For n = 7 and sum = 6, the pairs are (1, 5), (2, 4) and (3, 3), so the answer is 3. A pair may use the same number twice, which is why (3, 3) counts. The statement asks for O(n^2) time and O(1) extra space.
Approach: try every candidate once
Brute force means checking each candidate answer and keeping the ones that qualify. The candidates here are the pairs, so the question is how to list each of them exactly once.
If both numbers ran over 1 to n independently, every unordered pair with different members would be visited twice, as (1, 5) and as (5, 1). The fix is to require x <= y. The outer loop picks x from 1 to n, and the inner loop picks y from x to n. Starting y at x instead of x + 1 keeps pairs like (3, 3), where both numbers are equal.
Applied to the example, the pairs checked for x = 1 are (1, 1) to (1, 7), for x = 2 they are (2, 2) to (2, 7), and so on. Only three of them add up to 6.
Step by step
- Declare the counter.
let mut result: i32 = 0;starts at zero. It needsmutbecause it changes, andi32because that is the function's return type. - Loop over the first number with
for i in 1..=n. The range1..nwould stop beforen, but the problem includesn, so use the inclusive form1..=n. - Loop over the second number with
for j in i..=n, inside the first loop. Starting atiis what counts each pair once. - Test and count. If
i + j == sum, add one withresult += 1. Rust has no++operator. Every match is a different pair, so each one adds exactly one. - Return
resultafter both loops have finished.
The solution in Rust
impl Solution {
fn count_pairs(n: i32, sum: i32) -> i32 {
let mut result: i32 = 0;
for i in 1..=n {
for j in i..=n {
if i + j == sum {
result += 1;
}
}
}
return result;
}
}
Some Rust details:
- Ranges over
i32.1..=niteratesi32values here, becausenis ani32. No casts are needed, since nothing is used as an index. - Inclusive versus exclusive.
a..bexcludesbanda..=bincludes it. Ifnis smaller than the start (for examplej in i..=nwithi > n), the range is simply empty and the loop does not run. - Explicit return.
return result;works, and so would ending the function withresultand no semicolon, which is the more common Rust style. - Immutable by default. Without
mut,result += 1is a compile error: you can't assign twice to an immutable variable.
Tracing the example
With n = 7 and sum = 6, result starts at 0 and the loops proceed like this:
- For
i = 1,jruns from 1 to 7. Onlyj = 5gives 1 + 5 = 6, soresultbecomes 1. - For
i = 2,jruns from 2 to 7. Onlyj = 4matches, andresultbecomes 2. - For
i = 3,jruns from 3 to 7. Onlyj = 3matches, andresultbecomes 3. - For
i = 4, everyjis at least 4, so the sum is at least 8 and nothing matches. The same holds fori = 5, 6 and 7.
The loops finish and the function returns 3. The trace also shows a possible shortcut: once i + j passes sum, larger values of j can't match, so the inner loop could stop early. The lesson does not need it, and the full loop stays within the required O(n^2).
Complexity
Time: O(n^2). The inner loop runs n times for i = 1, n - 1 times for i = 2, and so on down to once for i = n. That totals n * (n + 1) / 2 checks, which grows like n^2. Each check is a single addition and comparison.
Space: O(1). The function keeps three integers, result, i and j, however large n is.
Edge cases and common mistakes
- The sum is too small. The smallest possible sum is 2, from
(1, 1). Withsum = 1nothing matches and the result is 0. - The sum is too large. The largest sum is
2 * n. Withn = 7,sum = 14gives 1 (the pair(7, 7)) andsum = 15gives 0. - A pair with equal members. For
n = 1andsum = 2the answer is 1, from(1, 1). Starting the inner loop ati + 1would miss it. - Counting both orders. Starting the inner loop at 1 gives 5 for
n = 7andsum = 6: the pairs(1, 5)and(5, 1),(2, 4)and(4, 2), and(3, 3). The answer should be 3. - Using
1..n. The exclusive range never triesn, so(7, 7)in the sum-14 case would be lost. - Integer size. The sum
i + jis at most2 * n, which fits in ani32whilenis below about 1.07 billion. Past that the addition would wrap silently in an optimized build, but a quadratic loop over that many values would never finish anyway. The counter itself can't overflow, because there are at mostnmatching pairs. - Large
n. The loops do work proportional ton^2, so very large inputs take a long time. The lesson's sizes are meant for this brute force.
Testing
n = 7,sum = 6gives 3.n = 10,sum = 11gives 5:(1, 10)to(5, 6).n = 10,sum = 12gives 5:(2, 10)to(6, 6).n = 1,sum = 2gives 1.n = 7,sum = 1gives 0.
