A clear explanation of designing a data structure that supports add and find operations for pair sums.
Problem Restatement
We need to design a data structure that supports two operations:
add(number)
find(value)The operations behave like this:
| Operation | Meaning |
|---|---|
add(number) | Add the number into the internal data structure |
find(value) | Return True if any pair of numbers sums to value |
The same number may be added multiple times.
The pair must use two different occurrences unless the number appears at least twice.
For example:
add(1)
add(3)
add(5)Then:
find(4) -> Truebecause:
1 + 3 = 4But:
find(7) -> Falsebecause no pair sums to 7.
Input and Output
| Method | Input | Output |
|---|---|---|
add | Integer number | None |
find | Integer value | Boolean |
Example class shape:
class TwoSum:
def add(self, number: int) -> None:
...
def find(self, value: int) -> bool:
...Examples
Example sequence:
twoSum = TwoSum()
twoSum.add(1)
twoSum.add(3)
twoSum.add(5)Now:
twoSum.find(4)returns:
Truebecause:
1 + 3 = 4Next:
twoSum.find(7)returns:
Falsebecause no valid pair exists.
Another example:
twoSum.add(3)
twoSum.add(3)Then:
twoSum.find(6)returns:
Truebecause two occurrences of 3 exist.
First Thought: Store All Numbers in a List
A direct solution is:
| Operation | Idea |
|---|---|
add | Append to a list |
find | Try every pair |
class TwoSum:
def __init__(self):
self.nums = []
def add(self, number: int) -> None:
self.nums.append(number)
def find(self, value: int) -> bool:
n = len(self.nums)
for i in range(n):
for j in range(i + 1, n):
if self.nums[i] + self.nums[j] == value:
return True
return FalseThis works, but find takes:
O(n^2)which is too slow.
We can do better with hashing.
Key Insight
Use a frequency map.
Store:
number -> countThen for each number x, the required partner is:
y = value - xThere are two cases.
| Case | Condition |
|---|---|
| Different numbers | x != y and y exists |
| Same number twice | x == y and count of x is at least 2 |
This lets us answer find in linear time.
Algorithm
Use a hash map:
self.countFor add(number):
- Increment its frequency.
For find(value):
- Loop through all stored numbers
x. - Compute:
y = value - x- If
x != y, returnTruewhenyexists. - If
x == y, returnTrueonly when the count is at least2.
If no valid pair exists, return False.
Walkthrough
Start:
add(1)
add(3)
add(5)Frequency map:
{
1: 1,
3: 1,
5: 1
}Call:
find(4)Check x = 1:
y = 4 - 1 = 3Since 3 exists, return:
TrueNow check:
find(7)Check:
x | y = 7 - x | Exists? |
|---|---|---|
1 | 6 | No |
3 | 4 | No |
5 | 2 | No |
Return:
FalseCorrectness
The frequency map stores exactly how many times each number has been added.
During find(value), for every stored number x, the algorithm checks whether the complementary value:
y = value - xexists.
If x != y, then one occurrence of x and one occurrence of y form a valid pair whose sum is value.
If x == y, then the pair uses the same value twice, so at least two occurrences are required. The algorithm checks this using the stored count.
If the algorithm returns True, a valid pair exists by construction.
If the algorithm finishes without returning True, then no stored number has a valid complementary partner, so no valid pair exists.
Therefore, the algorithm returns the correct result for every find call.
Complexity
Let n be the number of distinct stored values.
| Operation | Time | Space |
|---|---|---|
add | O(1) average | |
find | O(n) | |
| Total storage | O(n) |
Hash table operations are average-case constant time.
Implementation
class TwoSum:
def __init__(self):
self.count = {}
def add(self, number: int) -> None:
self.count[number] = self.count.get(number, 0) + 1
def find(self, value: int) -> bool:
for x in self.count:
y = value - x
if x != y:
if y in self.count:
return True
else:
if self.count[x] >= 2:
return True
return FalseCode Explanation
The constructor creates the frequency map:
self.count = {}The add operation increments the count:
self.count[number] = self.count.get(number, 0) + 1Inside find, loop through every stored value:
for x in self.count:Compute the needed partner:
y = value - xIf the two numbers are different:
if x != y:we only need the partner to exist:
if y in self.count:
return TrueIf the numbers are the same:
else:we need at least two copies:
if self.count[x] >= 2:
return TrueIf no valid pair is found:
return FalseAlternative Design: Fast find
We can also optimize for fast find.
Idea:
| Operation | Complexity |
|---|---|
add | O(n) |
find | O(1) |
Store every possible pair sum in a set.
When adding a new number, combine it with all previous numbers and store the sums.
Then find(value) becomes:
value in sumsThis is useful when find is called much more often than add.
Testing
def run_tests():
ts = TwoSum()
ts.add(1)
ts.add(3)
ts.add(5)
assert ts.find(4) is True
assert ts.find(7) is False
ts = TwoSum()
ts.add(3)
ts.add(3)
assert ts.find(6) is True
assert ts.find(7) is False
ts = TwoSum()
ts.add(-1)
ts.add(1)
assert ts.find(0) is True
ts = TwoSum()
ts.add(0)
ts.add(0)
assert ts.find(0) is True
ts = TwoSum()
ts.add(2)
ts.add(4)
ts.add(6)
assert ts.find(8) is True
assert ts.find(5) is False
print("all tests passed")
run_tests()| Test | Why |
|---|---|
1, 3, 5, find 4 | Standard example |
1, 3, 5, find 7 | No valid pair |
Two copies of 3 | Same value used twice |
| Negative and positive values | Mixed signs |
| Two zeros | Duplicate zero case |
| Multiple values | General behavior |