Trade memory for lookups. A scan that would otherwise need a second loop to ask "does this value exist anywhere else?" gets its answer in O(1) by remembering what it has already walked past - a set when only membership matters, a map when the position or count matters too.
The shape is almost always the same: one pass, one question per element, and the structure is updated as you go so that every answer it gives is about elements you have genuinely already seen.
Membership
Only one bit of information is needed per value - have I met it before? A set answers that and nothing else.
1 2 3 1 nums 1 2 3 seen already seen! duplicate found Tap to open Otto stashes each new value into the seen set as he passes it. When the second 1 arrives, the set already holds the answer.
217. Contains Duplicate
time space
Brute Force O(n2 ) time O(1) aux best space Sort and Compare Neighbors O(n + n log n) time O(sort) aux
Seen Set O(n) time best time O(n) aux Sliding Window↗ Sliding Window O(n) time best time O(n) aux FIG. 217 CONTAINS DUPLICATE ALL PAIRS ● INTERACTIVE
visualization loads as you reach it
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
n = len(nums)
for i in range(n):
for j in range(n):
if i != j and nums[i] == nums[j]:
return True
return False
How it worksO(n2 ) · O(1)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
Value to Index
The same pass, but the map remembers where each value was, so the match can be reported as a pair of positions rather than a yes or no.
1. Two Sum
time space
Brute Force O(n2 ) time O(1) aux best space 2-Pass Index Map O(2n) time O(n) aux
1-Pass Index Map O(n) time best time O(n) aux FIG. 1 TWO SUM ALL PAIRS ● INTERACTIVE
visualization loads as you reach it
class Solution:
def twoSum(self, nums: list[int], target: int) -> list[int]:
n = len(nums)
for i in range(n):
for j in range(i + 1, n):
if nums[i] + nums[j] == target:
return [i, j]
How it worksO(n2 ) · O(1)
Duplicates, collisions, and when sorting wins Duplicates overwrite, collisions crowd: only crowding hurts. A duplicate key overwrites its own entry in O(1). A collision is two different keys in one bucket; enough of them make every operation O(n). Say it as: "Expected O(n), worst O(n2 ) under adversarial collisions."
When sorting beats the map. The map is O(n) expected . With adversarial collisions every lookup costs O(n), so its worst case is O(n2 ). Sorting guarantees O(n log n), the best worst case available here. The catch: LeetCode 1 returns original indices and sorting scrambles them, so sort (value, index) pairs (O(n) extra space), then run two pointers. Once the input arrives sorted (167), the sort is free and the map loses outright.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
170. Two Sum III - Data structure design
170 Two Sum III - Data structure design LeetCode Design a data structure that accepts a stream of integers and checks if it has a pair of integers that sum up to a particular value. Implement the TwoSum class: TwoSum() initializes with an empty array, void add(int number) adds number to the data structure, and boolean find(int value) returns true if there exists any pair of numbers whose sum is equal to value, otherwise false.
["TwoSum", "add", "add", "add", "find", "find"] with [[], [1], [3], [5], [4], [7]] returns [null, null, null, null, true, false]: 1 + 3 = 4, and no pair sums to 7.
Constraints: -105 <= number <= 105 , -231 <= value <= 231 - 1, at most 104 calls in total to add and find.
time space
Set of Sums add O(n), find O(1) time O(n + min(n(n-1)/2, R)) aux
Count Map add O(1), find O(d) time best time O(d) aux Sorted List + 2 Converging Pointers↗ Two Pointers add O(log n + n), find O(n) time O(n) aux
FIG. 170 TWO SUM III SET OF SUMS ● INTERACTIVE
visualization loads as you reach it
class TwoSum:
def __init__(self):
self.arr = []
self.totals = set()
def add(self, number: int) -> None:
for num in self.arr:
self.totals.add(num + number)
self.arr.append(number)
def find(self, value: int) -> bool:
return value in self.totals
How it worksadd O(n), find O(1) · O(n + min(n(n-1)/2, R))
Every approach Design add find Space Set of Sums O(n) O(1) O(n + min(n(n-1)/2, R)) Count Map O(1) O(d) O(d) Sorted List + 2 Converging Pointers O(log n + n) O(n) O(n)
Which one depends on whether add or find calls dominate.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
Canonical Key
Sometimes the question is not "have I seen this value?" but "have I seen anything equivalent to this value?" Reduce each element to a canonical form - a form two equivalent elements always share - and use that as the key. The map then groups by equivalence without ever comparing two elements to each other.
eat tea ate aet sorted one bucket Tap to open Otto sorts each word's letters into one canonical key, so eat, tea and ate all land in the same bucket.
49. Group Anagrams
time space
Sorted String Key O(n * K log K) time O(sort + n * K) aux
Character Count Key O(26n + n * K) time best time O(26 + n * K) aux best space FIG. 49 GROUP ANAGRAMS SORTED KEY ● INTERACTIVE
visualization loads as you reach it
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
hm = collections.defaultdict(list)
for s in strs:
hm[tuple(sorted(s))].append(s)
return list(hm.values())
How it worksO(n * K log K) · O(sort + n * K)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
Bucketed Membership
One value can be a member of several groups at once, and has to be unique inside every one of them. Keep a separate set per group and derive each group's index from the element's position, so a single pass asks every question it needs to about a cell before committing it.
36. Valid Sudoku
time space
Sets per Row, Column and Box O(n2 ) time O(3n2 ) aux
Nine-Bit Masks O(n2 ) time O(3n) aux best space
Sets per Row, Column and Box
FIG. 36 VALID SUDOKU SETS ● INTERACTIVE
visualization loads as you reach it
class Solution:
def isValidSudoku(self, board: list[list[str]]) -> bool:
rows = collections.defaultdict(set)
cols = collections.defaultdict(set)
boxes = collections.defaultdict(set)
n = 9
for r in range(n):
for c in range(n):
cell = board[r][c]
if cell != ".":
b = 3 * (r // 3) + (c // 3)
if cell in rows[r] or cell in cols[c] or cell in boxes[b]:
return False
rows[r].add(cell)
cols[c].add(cell)
boxes[b].add(cell)
return True
How it worksO(n2 ) · O(3n2 )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
2133. Check if Every Row and Column Contains All Numbers
2133 Check if Every Row and Column Contains All Numbers LeetCode time space
One Set per Row and Column O(2n + n2 ) time best time O(2n2 ) aux Sign Flags In Place O(2n2 ) time O(1) aux best space
One Set per Row and Column
FIG. 2133 CHECK VALID SETS ● INTERACTIVE
visualization loads as you reach it
class Solution:
def checkValid(self, matrix: list[list[int]]) -> bool:
rows = collections.defaultdict(set)
cols = collections.defaultdict(set)
n = len(matrix)
for i in range(n):
for j in range(n):
cell = matrix[i][j]
rows[i].add(cell)
cols[j].add(cell)
for r in rows.values():
if len(r) != n:
return False
for c in cols.values():
if len(c) != n:
return False
return True
How it worksO(2n + n2 ) · O(2n2 )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
Value to Cell
When the input announces work by value but the question is asked about position , one prologue pass over the grid inverts it - every value becomes the coordinates of the single cell it names. The grid is then never read again, and the live pass is pure counting against that map.
2661. First Completely Painted Row or Column
2661 First Completely Painted Row or Column LeetCode
Map Values to Cells, Tally Rows and Columns
FIG. 2661 FIRST COMPLETE INDEX ● INTERACTIVE
visualization loads as you reach it
class Solution:
def firstCompleteIndex(self, arr: List[int], mat: List[List[int]]) -> int:
m, n = len(mat), len(mat[0])
hashmap = collections.defaultdict(tuple)
row = [0] * m
col = [0] * n
for i in range(m):
for j in range(n):
hashmap[mat[i][j]] = (i, j)
for idx, i in enumerate(arr):
r, c = hashmap[i]
row[r] += 1
col[c] += 1
if row[r] >= n or col[c] >= m:
return idx
return -1
How it worksO(2mn) · O(m + n + mn)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
Sum to Count
When the question is how many tuples hit a target, not which ones, split the tuple in half and store a count for every half-sum. A lookup of the negated half-sum then settles a whole group of tuples at once, and nothing needs deduping.
454. 4Sum II
time space
Meet in the Middle (2 + 2) O(2n2 ) time best time O(n2 ) aux best space Generalized kSum Count O(3n2 ) time O(2n2 ) aux
Meet in the Middle (2 + 2)
FIG. 454 4SUM II MEET IN THE MIDDLE ● INTERACTIVE
visualization loads as you reach it
class Solution:
def fourSumCount(
self, nums1: list[int], nums2: list[int], nums3: list[int], nums4: list[int]
) -> int:
seen = collections.Counter()
ans = 0
for a in nums1:
for b in nums2:
seen[a + b] += 1
for c in nums3:
for d in nums4:
complement = -c - d
ans += seen[complement]
return ans
How it worksO(2n2 ) · O(n2 )
Every approach Approach Time When an interviewer wants it Aux space Brute Force O(n4 ) opening line O(1) Count one array (1 + 3) O(n3 ) "memory is tight" O(n) Meet in the Middle (2 + 2) O(n2 ) the expected answer O(n2 ) Sorted Pair Sums + 2 Converging Pointers O(n2 log n) "no hashing" O(n2 ) Generalized kSum Count O(nk/2 ) "what about 6 arrays?" O(nk/2 )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑
kSum Count
Given k integer arrays arrays[0], ..., arrays[k - 1], each of length n, return the number of tuples (i_0, ..., i_{k-1}) such that arrays[0][i_0] + arrays[1][i_1] + ... + arrays[k-1][i_{k-1}] == 0.
This generalizes 454. 4Sum II from four arrays to any k. The solution below is written against the 4-array signature, and kSum(arrays) is the general part.
Example 1:
Input: k = 4, arrays = [[1, 2], [-2, -1], [-1, 2], [0, 2]]
Output: 2
Explanation: the two tuples are:
(0, 0, 0, 1): 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0): 2 + (-1) + (-1) + 0 = 0
Example 2:
Input: k = 4, arrays = [[0], [0], [0], [0]]
Output: 1
Example 3:
Input: k = 3, arrays = [[1, -1], [1, -1], [1, -1]]
Output: 0
Explanation: every sum is an odd count of +1 and -1, so it is odd and never 0.
Constraints:
1 <= k <= 6
1 <= n <= 30
-228 <= arrays[i][j] <= 228
The answer fits in a 64-bit integer.
class Solution:
def fourSumCount(
self, nums1: list[int], nums2: list[int], nums3: list[int], nums4: list[int]
) -> int:
def kSum(arrays):
def getCounter(arrays):
counter = collections.Counter()
for combo in itertools.product(*arrays):
counter[sum(combo)] += 1
return counter
half = len(arrays) // 2
left, right = getCounter(arrays[:half]), getCounter(arrays[half:])
ans = 0
for total, count in left.items():
ans += count * right[-total]
return ans
return kSum([nums1, nums2, nums3, nums4])
How it worksO(a * na + b * nb + na + k * n) · O(na + nb + k * n + k)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - top ↑