Hash Map / Set

Easy
Time: O(n) averageSpace: O(n)
0/6
problems solved
0%
1.

Why Hash Maps Are Your Best Friend

Hash maps (dictionaries) and sets are the most versatile data structures in coding interviews. They give you O(1) average time for insert, delete, and lookup operations.

Think of a hash map as a super-powered array where you can use ANY value as an index, not just integers 0 to n-1. Instead of array[5], you can do map['hello'] or map[someObject].

When to immediately think 'hash map':

  • 'Find if X exists' -> Set for O(1) lookup
  • 'Find pair that sums to target' -> Map to store complements
  • 'Count occurrences' -> Map as frequency counter
  • 'Group items by property' -> Map with computed key
  • 'Check for duplicates' -> Set

The pattern recognition is crucial: whenever you see O(n²) nested loops checking 'have I seen this before?', a hash map can often reduce it to O(n).

2.

The Two Sum Pattern: Complement Lookup

Two Sum is THE classic hash map problem. Given an array and a target sum, find two numbers that add up to the target.

The brute force approach checks every pair: O(n²). But with a hash map, we can do it in O(n).

The key insight: for each number X, we need to find if (target - X) exists. Instead of scanning the array each time, store what we've seen in a hash map.

Critical detail: Check for complement BEFORE adding current number to the map. Otherwise, you might match an element with itself.

This pattern extends to:

  • 3Sum, 4Sum (combine with two pointers)
  • Finding pairs with any relationship (difference, product)
  • Subarray sum problems (using prefix sums)
Code
Java

Interactive Two Sum

Two Sum: Complement Lookup

Use HashMap to find pair summing to target in O(n)

Target: 9
Speed:
nums = [2, 7, 11, 15, 3, 6]
i=02
i=17
i=211
i=315
i=43
i=56
HashMap: value → index
Empty - will store {value: index}
complement = 9 - 2 = 7
Click Play to find two numbers that sum to target
Current
In HashMap
Found Pair

Key Insight: For each number, check if its complement (target - num) exists in the map. Check BEFORE adding to avoid matching element with itself.

3.

Frequency Counting

Many problems require counting how often elements appear. A hash map is perfect for this.

Building a frequency map:

  1. Iterate through elements
  2. For each element, increment its count in the map
  3. Use getOrDefault (Java) or || 0 (JS) for elements not yet seen

Pro tip for characters: When counting lowercase letters, use an int[26] array instead of a hash map. It's faster and uses less memory. The index is calculated as: char - 'a'.

This pattern is the foundation for:

  • Top K Frequent Elements
  • Valid Anagram
  • First Unique Character
  • Majority Element
  • Sort Characters by Frequency
Code
Java
4.

Grouping by Key: Group Anagrams

Sometimes you need to group elements that share a common property. The key insight is choosing the RIGHT key for your hash map.

For Group Anagrams, all anagrams share the same letters - just in different order. So we need a 'canonical form' that's the same for all anagrams:

Option 1: Sort the string

  • 'eat', 'tea', 'ate' all become 'aet'
  • Simple but O(k log k) per string where k is string length

Option 2: Character count as key

  • 'eat' -> '#1#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#0#0#0#0#0#0'
  • O(k) per string, but longer key

This pattern works for any grouping problem:

  • Group by pattern (Word Pattern)
  • Group by transformation (Isomorphic Strings)
  • Group by any computed property
Code
Java

Interactive Anagram Grouping

Group Anagrams

Use sorted string as key to group anagrams together

Speed:
Input words:
eat
tea
tan
ate
nat
bat
HashMap: sorted_key → [words]
Empty - groups will appear here
6
Total Words
0
Processed
0
Groups Found
Click Play to group anagrams

Key Insight: Anagrams have the same characters, so sorting them produces the same key. Use this sorted string as the HashMap key to group all anagrams together.

5.

Longest Consecutive Sequence

This problem showcases the power of Set for O(1) lookups. Find the length of the longest consecutive sequence in an unsorted array.

Brute force: Sort the array first, then scan for consecutive runs. O(n log n).

Hash Set approach: O(n) time!

The trick: Only start counting from the BEGINNING of a sequence. How do we know if a number starts a sequence? Check if (num - 1) exists - if not, this number is a sequence start.

For each sequence start, keep checking if (num + 1), (num + 2), etc. exist. This looks like O(n²), but each number is only visited once as part of exactly one sequence, so it's O(n) total.

Code
Java

Interactive Consecutive Sequence

Longest Consecutive Sequence

Use HashSet to find consecutive sequences in O(n)

Speed:
Original array (unsorted):
Sorted view (for visualization only):
0
Longest Found
0
Sequences Found
Click Play to find longest consecutive sequence
Checking
In Sequence
Not Start

Key Insight:Only start counting from sequence beginnings (numbers where n-1 doesn't exist). Each number is visited at most twice: once in scan, once when extending a sequence. O(n) total.

6.

Duplicate Detection

Sets are perfect for tracking what you've seen. The add() operation returns false if the element already exists - a one-liner for duplicate detection!

Contains Duplicate: Does any value appear twice?

  • Add each element to a set
  • If add() returns false (already exists), we found a duplicate

Contains Duplicate II: Are there duplicates within k indices of each other?

  • Store index in a map, or use a sliding window set of size k

Find All Duplicates: Which values appear exactly twice? (Array values are 1 to n)

  • Clever trick: Use the array itself as a hash map
  • For value v, mark index (v-1) as negative
  • If already negative, v is a duplicate
Code
Java
7.

Set Operations: Intersection and Union

Sets give you O(1) membership testing, making intersection and union operations efficient.

Intersection of Two Arrays:

  1. Put all elements of array1 into a Set
  2. For each element in array2, check if it's in the Set
  3. If yes, add to result (use another Set to avoid duplicates)

Happy Number (uses Set for cycle detection): A number is 'happy' if repeatedly summing squares of its digits eventually reaches 1. If it loops forever, it's not happy.

Use a Set to detect cycles - if we see the same number twice, we're in a loop.

Code
Java
8.

Choosing the Right Hash Structure

Quick reference for which hash structure to use:

NeedUseExample
Just check existenceSetContains duplicate, valid sudoku
Map value to somethingMap<K, V>Two Sum (val -> index)
Count occurrencesMap<K, Integer>Frequency counter
Group itemsMap<K, List<V>>Group anagrams
Character frequency (a-z)int[26]Valid anagram
Preserve insertion orderLinkedHashMapLRU Cache
Sorted keysTreeMapCalendar, intervals

Common pitfalls:

  • HashMap operations are O(1) average but O(n) worst case (hash collisions)
  • Custom objects as keys need proper hashCode() and equals()
  • In JavaScript, objects as Map keys use reference equality
  • Sets don't maintain order (use LinkedHashSet if needed)
9.

First Unique Character

Find the first non-repeating character in a string. A perfect frequency counting problem.

Two-Pass Approach:

  1. First pass: Count frequency of each character
  2. Second pass: Return first character with count = 1

Example: "leetcode"

Pass 1 - Build frequency map:
l:1, e:3, t:1, c:1, o:1, d:1
Pass 2 - Find first with count 1:
index 0: 'l' count=1 → Found! Return 0

Example: "loveleetcode"

Frequency: l:2, o:2, v:1, e:4, t:1, c:1, d:1
Scan: l(2), o(2), v(1) → Return index 2

Why Two Passes? We can't return on first occurrence because we don't know if it repeats later. We need full frequency information before deciding.

Code
Java
10.

Valid Sudoku: Multi-Dimensional Tracking

Check if a 9x9 Sudoku board is valid. Use sets to track what we've seen in each row, column, and 3x3 box.

The Key Insight: We need to check three constraints simultaneously:

  1. Each row contains 1-9 with no duplicates
  2. Each column contains 1-9 with no duplicates
  3. Each 3x3 box contains 1-9 with no duplicates

Box Index Formula: For cell at (row, col), box index = (row / 3) * 3 + (col / 3)

Box indices:
[0][1][2]
[3][4][5]
[6][7][8]
Cell (4, 7) → box = (4/3)*3 + (7/3) = 1*3 + 2 = 5

Encoding Trick: Instead of 27 separate sets, use one set with encoded strings:

  • "row 0 has 5"
  • "col 3 has 5"
  • "box 4 has 5"
Code
Java
11.

Word Pattern & Isomorphic Strings

Check if two sequences follow the same pattern - a bijection (one-to-one mapping) problem.

Word Pattern: Does "dog cat cat dog" match pattern "abba"? Isomorphic Strings: Does "egg" map to "add"?

Key Insight: We need BIDIRECTIONAL mapping:

  • Each pattern char maps to exactly one word
  • Each word maps to exactly one pattern char

Example: pattern="abba", s="dog cat cat dog"

a → dog (new mapping)
b → cat (new mapping)
b → cat (matches existing) ✓
a → dog (matches existing) ✓
Result: true

Example: pattern="abba", s="dog cat cat fish"

a → dog
b → cat
b → cat ✓
a → fish (CONFLICT! a already maps to dog)
Result: false

Why Two Maps? With only pattern→word, "abba" would incorrectly match "dog dog dog dog" (a→dog, b→dog both work one-way but violate bijection).

Code
Java
12.

Subarray Sum Equals K with HashMap

Count subarrays with sum exactly K. This combines prefix sum with HashMap lookup.

The Brilliant Insight: If prefix[j] - prefix[i] = k, then subarray [i+1, j] has sum k.

Rearranging: prefix[i] = prefix[j] - k

So for each position j, we ask: "How many earlier prefix sums equal (currentSum - k)?"

Visual Trace: nums = [1, 2, 3], k = 3

Initialize: prefixCount = {0: 1}, sum = 0, count = 0
i=0, num=1:
sum = 1
Look for sum - k = 1 - 3 = -2 → not found
prefixCount = {0: 1, 1: 1}
i=1, num=2:
sum = 3
Look for sum - k = 3 - 3 = 0 → found 1 time!
count = 1 (subarray [1,2])
prefixCount = {0: 1, 1: 1, 3: 1}
i=2, num=3:
sum = 6
Look for sum - k = 6 - 3 = 3 → found 1 time!
count = 2 (subarray [3])
prefixCount = {0: 1, 1: 1, 3: 1, 6: 1}
Answer: 2 subarrays

Critical: Initialize with {0: 1} to count subarrays starting at index 0.

Code
Java
13.

LRU Cache: LinkedHashMap Magic

Design a Least Recently Used (LRU) cache with O(1) get and put operations.

Requirements:

  • get(key): Return value if exists, mark as recently used
  • put(key, value): Add/update, evict LRU item if at capacity

Data Structure: Combine HashMap (O(1) lookup) + Doubly Linked List (O(1) reorder):

  • HashMap: key → Node
  • DLL: maintains usage order (head = most recent, tail = LRU)

Operations:

get(key):
1. Look up in HashMap → O(1)
2. Move node to head of DLL → O(1)
3. Return value
put(key, value):
1. If exists: update value, move to head
2. If new: create node, add to head
3. If over capacity: remove tail node

Java Shortcut: LinkedHashMap with accessOrder=true does this automatically!

Code
Java
14.

Top K Frequent Elements

Find the k most frequent elements. Combines frequency counting with selection.

Three Approaches:

1. Heap Approach - O(n log k)

  • Build frequency map
  • Use min-heap of size k
  • Keep k most frequent

2. Bucket Sort - O(n)

  • Build frequency map
  • Create buckets where index = frequency
  • Collect from highest buckets

3. Quick Select - O(n) average

  • Similar to finding kth element
  • More complex to implement

Bucket Sort Visualization:

nums = [1,1,1,2,2,3], k = 2
Frequency: {1: 3, 2: 2, 3: 1}
Buckets (index = frequency):
[0]: []
[1]: [3]
[2]: [2]
[3]: [1]
[4]: []
[5]: []
[6]: []
Collect from right: [1, 2]
Code
Java
15.

Ransom Note

Can we construct ransom note from magazine letters? A simple frequency comparison.

Algorithm:

  1. Count letter frequencies in magazine
  2. For each letter in ransom note, decrement count
  3. If any count goes negative, return false

Optimization: Use int[26] instead of HashMap for lowercase letters.

Example:

ransomNote = "aa", magazine = "aab"
magazine freq: {a: 2, b: 1}
Process ransom note:
'a': freq[a] = 2-1 = 1 ✓
'a': freq[a] = 1-1 = 0 ✓
Result: true
Code
Java
16.

Pattern Recognition Guide

Quick reference for HashMap/Set problems:

Problem SignalTechniqueExample
"Find if exists"Set lookupContains duplicate
"Find pair with sum/diff"Complement lookupTwo Sum
"Count occurrences"Frequency mapTop K frequent
"Group by property"Map<key, List>Group anagrams
"Check duplicates within distance"Sliding window setContains Duplicate II
"One-to-one mapping"Two maps (bidirectional)Isomorphic strings
"Longest without repeats"Set + sliding windowLongest substring
"Subarray with sum K"Prefix sum + mapSubarray Sum Equals K
"Recently used ordering"LinkedHashMap / MapLRU Cache
"Multi-constraint tracking"Encoded keys in SetValid Sudoku

Decision Tree:

Need O(1) lookup?
├── Just existence → Set
└── Need associated value → Map
├── Count frequency → Map<T, Integer>
├── Group items → Map<Key, List<T>>
├── Track index → Map<T, Integer>
└── Bidirectional → Two Maps
Lowercase letters only?
└── Use int[26] instead of Map (faster)
Need ordering?
├── Insertion order → LinkedHashMap
└── Sorted order → TreeMap
17.

Common Mistakes and Tips

Mistake 1: Forgetting Initialization

// WRONG: NullPointerException
Map<String, List<String>> groups = new HashMap<>();
groups.get(key).add(value); // NPE if key doesn't exist!
// CORRECT: Initialize before adding
groups.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
// Or in JavaScript:
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(value);

Mistake 2: Modifying While Iterating

// WRONG: ConcurrentModificationException
for (Integer key : map.keySet()) {
if (condition) map.remove(key); // Throws!
}
// CORRECT: Use iterator or collect keys first
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
if (condition) it.remove();
}

Mistake 3: Using Mutable Objects as Keys

// DANGEROUS: Array hashCode is identity-based
Map<int[], Integer> map = new HashMap<>();
int[] arr = {1, 2, 3};
map.put(arr, 100);
map.get(new int[]{1, 2, 3}); // Returns null!
// CORRECT: Convert to immutable key
String key = Arrays.toString(arr); // "[1, 2, 3]"

Mistake 4: Not Using getOrDefault

// VERBOSE
int count = map.containsKey(key) ? map.get(key) : 0;
// CLEAN
int count = map.getOrDefault(key, 0);

Pro Tips:

  1. For character counting, prefer int[26] over HashMap
  2. Use merge() for frequency counting: map.merge(key, 1, Integer::sum)
  3. JavaScript Map preserves insertion order (useful for LRU)
  4. Check for null/undefined before calling methods on map values
18.

Complexity Analysis

Time Complexity:

OperationHashMapTreeMap
putO(1) avgO(log n)
getO(1) avgO(log n)
removeO(1) avgO(log n)
containsKeyO(1) avgO(log n)

Worst Case: HashMap operations can be O(n) with bad hash function (all collisions). Java 8+ uses trees for buckets with many collisions, improving to O(log n).

Space Complexity:

  • HashMap: O(n) for n entries
  • int[26]: O(1) for character counting
  • LinkedHashMap: O(n) with extra pointer overhead

When to Use What:

int[] array (fixed size):
- Fastest for small fixed key sets
- Character frequency (a-z): int[26]
- Digit counting (0-9): int[10]
HashMap:
- General purpose O(1) operations
- Unknown or large key sets
- Keys can be any hashable object
LinkedHashMap:
- Need insertion order (LRU cache)
- Iteration in predictable order
TreeMap:
- Need sorted key order
- Range queries (subMap, headMap)
- Floor/ceiling operations

Memory Comparison (approximate):

int[26]: ~104 bytes
HashMap (26 char): ~1-2 KB (object overhead)
TreeMap (26 char): ~1-2 KB
19.

Template Summary

Template 1: Frequency Counter

Map<T, Integer> freq = new HashMap<>();
for (T item : items) {
freq.merge(item, 1, Integer::sum);
}

Template 2: Two Sum / Complement Lookup

Map<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (seen.containsKey(complement)) {
return new int[]{seen.get(complement), i};
}
seen.put(nums[i], i);
}

Template 3: Grouping

Map<K, List<V>> groups = new HashMap<>();
for (V item : items) {
K key = computeKey(item);
groups.computeIfAbsent(key, k -> new ArrayList<>()).add(item);
}

Template 4: Character Frequency (Optimized)

int[] freq = new int[26];
for (char c : s.toCharArray()) {
freq[c - 'a']++;
}

Template 5: Sliding Window with Set

Set<T> window = new HashSet<>();
int left = 0;
for (int right = 0; right < n; right++) {
while (window.contains(arr[right])) {
window.remove(arr[left++]);
}
window.add(arr[right]);
// Process valid window
}

Template 6: Bidirectional Mapping

Map<A, B> aToB = new HashMap<>();
Map<B, A> bToA = new HashMap<>();
// Check both directions before adding
if (aToB.containsKey(a) && !aToB.get(a).equals(b)) return false;
if (bToA.containsKey(b) && !bToA.get(b).equals(a)) return false;
aToB.put(a, b);
bToA.put(b, a);

Test Your Knowledge

Take a quick 15question quiz to test what you've learned.