◀ THE GRIND — ARRAYS & HASHING

Design HashSet

The drill: Build a set of non-negative integers from scratch — add, remove, and check membership — without reaching for a language's built-in hash set.

THE BRIEFING — THE FULL DRILL, IN MY OWN WORDS

A set of non-negative integers needs building from the ground up — add a value, remove a value, and check whether a value is currently present — without leaning on a language's ready-made hash set.

Adding a value already inside does nothing new, and removing a value that isn't present is simply a no-op; the set only ever tracks presence, never how many times something was added.

This site drives the drill as a sequence of operations against one live instance: each call acts on whatever the set currently holds, in the order the calls arrive.

EX 01
MyHashSet()
add(1)
add(2)
contains(1) → true
contains(3) → false
add(2)
contains(2) → true
remove(2)
contains(2) → false
BASIC ADD, CONTAINS, REMOVE
EX 02
MyHashSet()
add(5)
add(5)
remove(5)
contains(5) → false
remove(5)
ADDING THE SAME KEY TWICE IS IDEMPOTENT; REMOVING TWICE IS A NO-OP
EX 03
MyHashSet()
add(0)
contains(0) → true
add(1000000)
contains(1000000) → true
remove(0)
contains(0) → false
BOUNDARY KEYS: 0 AND 1,000,000
THE HINTS — TAKE ONLY WHAT YOU NEED
HINT 1 THE NUDGE

A plain list gets add and remove for free, but membership means scanning every element. How do you make 'have I seen this' stop depending on how many things you've stored?

HINT 2 THE STRUCTURE

Keys live in a bounded range. A fixed-size array of buckets, indexed by key modulo the bucket count, turns 'which bucket' into one division.

HINT 3 ONE STEP FROM THE ANSWER

Each bucket only ever holds the handful of keys that collide into it — a short list per bucket, scanned only within that bucket, keeps every operation close to O(1).

COACH'S BOARD — THE PATTERN, STEP BY STEP
THE BUCKET LINEPATTERN · BUCKETED HASH TABLE — OPSadd 1, add 2, contains 1, contains 3, add 2, contains 2, remove 2, contains 2
add 1
add 2
contains 1
contains 3
add 2
contains 2
remove 2
contains 2
SET CONTENTS
set{}
STEP 1

Ops run against one live set, hashed into fixed buckets by key mod table size.

STEP 1 / 12 · ← → WORK TOO
THE SPLITS — TWO PACES, TWO LANGUAGES
grind/design-hashset.pyRACE PACE
LANG ▸
PACE ▸
class MyHashSet:
    def __init__(self):
        self.buckets = 1000
        self.table = [[] for _ in range(self.buckets)]

    def _bucket(self, key: int) -> list:
        return self.table[key % self.buckets]

    def add(self, key: int) -> None:
        bucket = self._bucket(key)
        if key not in bucket:
            bucket.append(key)

    def remove(self, key: int) -> None:
        bucket = self._bucket(key)
        if key in bucket:
            bucket.remove(key)

    def contains(self, key: int) -> bool:
        return key in self._bucket(key)
TIME O(1) AVERAGE PER OPSPACE O(N + BUCKETS)PYTHON · RACE PACE · 20 LN

✓ CHIP-TIMED — ALL 4 SOLUTIONS RAN GREEN AGAINST SELF-AUTHORED CASES IN CI · JDK 21 · CPYTHON 3.12 · NOTHING PUBLISHES RED