Pattern Reference
Hashmap Patterns
"Frequency counting, anagram grouping, subarray sum equals k, longest consecutive sequence."
Loading...
Deep Dive Tutorial
A hash map gives O(1) average lookup. Most O(n²) problems — "find a pair," "count occurrences," "group by property" — become O(n) when you store the result of the first pass in a map and query it in the second pass (or combine both passes into one). The key is recognizing WHAT to store.
| Problem Signal | What to Store | Pattern |
|---|---|---|
| "find pair with sum = target" | complement (target - x) | Two-sum: store x, query target-x |
| "count occurrences" | frequency of each element | freq[x]++ for every x |
| "group by property" | sorted form / canonical key | map[canonical].push(item) |
| "longest subarray with sum k" | first index of each prefix sum | prefix + map[sum-k] |
| "check all chars in window" | need/have count | two-pointer + freq map |
| "find duplicate" | seen elements | if set.has(x) → duplicate |
The Two-Sum Template
Two-sum and its variants
// Classic two-sum: find indices i,j where nums[i]+nums[j]=target
var twoSum = function(nums, target) {
const map = new Map(); // value → index
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) return [map.get(complement), i];
map.set(nums[i], i);
}
};
// Variant: count pairs with sum = target (allow reuse)
const countPairs = (nums, target) => {
const freq = new Map();
let count = 0;
for (const n of nums) {
count += freq.get(target - n) ?? 0; // query BEFORE adding
freq.set(n, (freq.get(n) ?? 0) + 1);
}
return count;
};
// Variant: two-sum with sorted array (two pointers — O(1) space)
const twoSumSorted = (arr, target) => {
let lo = 0, hi = arr.length - 1;
while (lo < hi) {
const s = arr[lo] + arr[hi];
if (s === target) return [lo, hi];
if (s < target) lo++; else hi--;
}
return [-1, -1];
};Frequency Counting Template
Build frequency map, query it
// Build frequency map
const freq = new Map();
for (const c of s) freq.set(c, (freq.get(c) ?? 0) + 1);
// Check if two strings are anagrams
const isAnagram = (s, t) => {
if (s.length !== t.length) return false;
const freq = new Map();
for (const c of s) freq.set(c, (freq.get(c) ?? 0) + 1);
for (const c of t) {
if (!freq.get(c)) return false;
freq.set(c, freq.get(c) - 1);
}
return true;
};
// Group by canonical form (anagram groups)
const groupAnagrams = (strs) => {
const map = new Map();
for (const s of strs) {
const key = s.split('').sort().join(''); // canonical key
if (!map.has(key)) map.set(key, []);
map.get(key).push(s);
}
return [...map.values()];
};Prefix Sum + HashMap (Running State)
Longest subarray with sum k using prefix map
// Find longest subarray with sum exactly k
// Works with NEGATIVES (sliding window doesn't)
var maxSubArrayLen = function(nums, k) {
const firstSeen = new Map([[0, -1]]); // prefix sum 0 at index -1
let sum = 0, maxLen = 0;
for (let i = 0; i < nums.length; i++) {
sum += nums[i];
if (firstSeen.has(sum - k)) maxLen = Math.max(maxLen, i - firstSeen.get(sum - k));
if (!firstSeen.has(sum)) firstSeen.set(sum, i); // store FIRST occurrence only
}
return maxLen;
};
// Note: store FIRST occurrence (for longest subarray)
// For COUNT: store count of occurrencesWorked Problems
brain
HashMap decision guide:
- "Find pair" → store complement, query on next element
- "Count/group by property" → map[canonical_key] += 1 or push
- "Longest subarray with sum k" → prefix sum map (works with negatives)
- "Running state" (valid chars, balance) → track in map as window moves
- "First/last occurrence" → map stores index; for longest, store first; for count, store count
- "Find pair" → store complement, query on next element
- "Count/group by property" → map[canonical_key] += 1 or push
- "Longest subarray with sum k" → prefix sum map (works with negatives)
- "Running state" (valid chars, balance) → track in map as window moves
- "First/last occurrence" → map stores index; for longest, store first; for count, store count