Patterns/Part III - Hashing & Auxiliary Structures/Hashmap Patterns

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 SignalWhat to StorePattern
"find pair with sum = target"complement (target - x)Two-sum: store x, query target-x
"count occurrences"frequency of each elementfreq[x]++ for every x
"group by property"sorted form / canonical keymap[canonical].push(item)
"longest subarray with sum k"first index of each prefix sumprefix + map[sum-k]
"check all chars in window"need/have counttwo-pointer + freq map
"find duplicate"seen elementsif 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 occurrences

Worked 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