DSABeginner6 min readPart 2 of 16

Hash Maps

Hash maps (also called hash tables, dictionaries or objects) are the most useful data structure in coding interviews. They look up, insert and delete in O(1) on average. If you reach for a hash map in 30–40% of your solutions, you are on the right track.

Can you explain hash maps out loud?

I'm Alex. Read this, then I'll interview you on real problems and push on the parts you skip.

How a lookup worksCore idea

A hash map turns a key into an array index using a hash function: a function that maps any key to a number in the range 0 to capacity − 1. The value is stored at that index, so finding it later is one calculation and one jump.

%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","actorBkg":"#e7f5ff","actorBorder":"#1e1e1e","noteBkgColor":"#fff9db","noteBorderColor":"#1e1e1e"}}}%%
graph LR
  K["key: 'cat'"] --> H["hash('cat') % 16"]
  H --> B["bucket[5]"]
  B --> C{"key there?"}
  C -->|yes| V["return value"]
  C -->|no| M["return undefined"]
  class K orange
  class H yellow
  class B blue
  class V green
  class M red
classDef orange fill:#ffd8a8,stroke:#1e1e1e,color:#1e1e1e
classDef blue fill:#a5d8ff,stroke:#1e1e1e,color:#1e1e1e
classDef green fill:#b2f2bb,stroke:#1e1e1e,color:#1e1e1e
classDef red fill:#ffc9c9,stroke:#1e1e1e,color:#1e1e1e
classDef violet fill:#d0bfff,stroke:#1e1e1e,color:#1e1e1e
classDef yellow fill:#ffec99,stroke:#1e1e1e,color:#1e1e1e
classDef grey fill:#e9ecef,stroke:#1e1e1e,color:#1e1e1e
DiagramLooking up a key: hash it, jump to the bucket, check whether the key is there.
A simplified hash mapTypeScript
class HashMap {
  private buckets: Array<[string, any][]> = Array(16).fill(null).map(() => []);

  set(key: string, value: any) {
    const index = this.hash(key) % this.buckets.length;
    const bucket = this.buckets[index];
    const existing = bucket.find(([k]) => k === key);
    if (existing) existing[1] = value;
    else bucket.push([key, value]);
  }

  get(key: string): any {
    const index = this.hash(key) % this.buckets.length;
    const bucket = this.buckets[index];
    const pair = bucket.find(([k]) => k === key);
    return pair ? pair[1] : undefined;
  }

  private hash(key: string): number {
    let hash = 0;
    for (const char of key) hash = (hash * 31 + char.charCodeAt(0)) % 2147483647;
    return Math.abs(hash);
  }
}

Collision handlingHow it works

Two different keys can land on the same index. That is called a collision, and there are two main ways to deal with it:

%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","actorBkg":"#e7f5ff","actorBorder":"#1e1e1e","noteBkgColor":"#fff9db","noteBorderColor":"#1e1e1e"}}}%%
graph LR
  B0["bucket 4"] --> E0["empty"]
  B1["bucket 5"] --> N1["'cat' → 3"] --> N2["'act' → 7"]
  B2["bucket 6"] --> N3["'dog' → 1"]
  class B0,B1,B2 blue
  class N1,N2 orange
  class N3 green
  class E0 grey
classDef orange fill:#ffd8a8,stroke:#1e1e1e,color:#1e1e1e
classDef blue fill:#a5d8ff,stroke:#1e1e1e,color:#1e1e1e
classDef green fill:#b2f2bb,stroke:#1e1e1e,color:#1e1e1e
classDef red fill:#ffc9c9,stroke:#1e1e1e,color:#1e1e1e
classDef violet fill:#d0bfff,stroke:#1e1e1e,color:#1e1e1e
classDef yellow fill:#ffec99,stroke:#1e1e1e,color:#1e1e1e
classDef grey fill:#e9ecef,stroke:#1e1e1e,color:#1e1e1e
DiagramChaining: each bucket keeps a small list, so two keys can share bucket 5.

Chainingmost common

Each bucket stores a linked list (or a balanced tree). A collision just adds to the list. Most real implementations do this.

Open addressingprobing

If a slot is taken, try the next one (linear probing) or jump by a growing step (quadratic). Used in some high-performance maps.

Time complexityCompare

OperationAverageWorst
GetO(1)O(n)
SetO(1)O(n)
DeleteO(1)O(n)
ContainsO(1)O(n)

Pattern 1: Frequency countingPattern

Count how often each thing appears. It is the base of anagram, duplicate and “most frequent” problems.

freq for "banana"ban132One pass over the string fills the counts.
DiagramCharacter counts for “banana”.
Count character frequenciesTypeScript
function charFrequency(s: string): Map<string, number> {
  const freq = new Map<string, number>();
  for (const ch of s) {
    freq.set(ch, (freq.get(ch) || 0) + 1);
  }
  return freq;
}
TimeO(n)SpaceO(k) distinct keys

Pattern 2: Two Sum (remember what you have seen)Pattern

Store each value as you pass it. For every new value, check whether its partner is already stored.

Find a pair that sums to targetTypeScript
function twoSum(nums: number[], target: number): [number, number] {
  const seen = new Map<number, number>();
  for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];
    if (seen.has(complement)) return [seen.get(complement)!, i];
    seen.set(nums[i], i);
  }
  throw new Error("No pair found");
}
TimeO(n)SpaceO(n)

Pattern 3: GroupingPattern

Build a key that is the same for everything in a group, then collect items under it. For anagrams, the sorted letters make a good key: “eat”, “tea” and “ate” all become “aet”.

%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","actorBkg":"#e7f5ff","actorBorder":"#1e1e1e","noteBkgColor":"#fff9db","noteBorderColor":"#1e1e1e"}}}%%
graph LR
  W1["eat"] --> K1["aet"]
  W2["tea"] --> K1
  W3["ate"] --> K1
  W4["tan"] --> K2["ant"]
  W5["nat"] --> K2
  W6["bat"] --> K3["abt"]
  class W1,W2,W3,W4,W5,W6 blue
  class K1 orange
  class K2 green
  class K3 violet
classDef orange fill:#ffd8a8,stroke:#1e1e1e,color:#1e1e1e
classDef blue fill:#a5d8ff,stroke:#1e1e1e,color:#1e1e1e
classDef green fill:#b2f2bb,stroke:#1e1e1e,color:#1e1e1e
classDef red fill:#ffc9c9,stroke:#1e1e1e,color:#1e1e1e
classDef violet fill:#d0bfff,stroke:#1e1e1e,color:#1e1e1e
classDef yellow fill:#ffec99,stroke:#1e1e1e,color:#1e1e1e
classDef grey fill:#e9ecef,stroke:#1e1e1e,color:#1e1e1e
DiagramEvery word is filed under its sorted letters.
Group anagrams togetherTypeScript
function groupAnagrams(strs: string[]): string[][] {
  const map = new Map<string, string[]>();
  for (const s of strs) {
    const key = s.split('').sort().join('');
    if (!map.has(key)) map.set(key, []);
    map.get(key)!.push(s);
  }
  return Array.from(map.values());
}
TimeO(n · k log k)SpaceO(n · k)

Common mistakesPitfalls

Using a hash map when order mattersHash maps do not keep keys sorted. Use an array or an ordered map instead.
Ignoring spacen entries cost O(n) memory. If the input is sorted, two pointers may do it in O(1).
The wrong key typePlain JavaScript objects only use string keys. Use Map for anything else.
Not handling a missing keyCheck whether get() returned undefined before using the value.
OvercomplicatingIf the brute force is a nested loop, try a hash map before anything fancier.

In the interviewIn the interview

Trading memory for speed is the most common optimisation interviewers expect. Say the trade out loud.

Frequently asked questionsFAQ

What is the time complexity of a hash map lookup?

The average case is O(1) for insert, lookup, and delete because a hash function maps a key directly to a bucket. The worst case degrades to O(n) when many keys collide into one bucket, which is why load factors, resizing, and collision strategy keep the average constant.

When should you use a hash map instead of an array?

Use a hash map when keys are arbitrary values like strings, or when the key space is large and sparse. Use an array when keys are small dense integers, for example a character frequency table of size 26, which gives O(1) space and avoids hashing overhead.

How do hash maps handle collisions?

Two common strategies are chaining, storing colliding keys in a list at the same bucket, and open addressing, probing to the next free slot. Modern languages like Java and Go use chaining with a balanced-tree fallback for oversized buckets so worst-case lookup stays O(log n).

Now explain hash maps to me out loud

I'll give you interview problems and ask follow-ups, like a real interviewer.

Practice with Alex

Keep readingKeep reading

Ready to practice?

Reading is step one. Saying it out loud is step two.

Start a mock interview with Alex, get pushed on the parts you skip, and see your hiring signal right after.