Exercises — Deletion in open addressing — tombstone markers
The three slot states we use everywhere: EMPTY (never held a key — search STOPS here), OCCUPIED (holds a live key), DELETED (a tombstone — a key was removed but search KEEPS GOING). Probe sequence for linear probing is
We write † for a tombstone slot and _ for EMPTY.
Before the first exercise, anchor the three states to a picture. Figure s01 shows a size-7 table with one of each state. Trace the magenta arrow: a search that starts left of the tombstone and walks straight past it — that "walk past" is the entire idea the exercises drill.

† (magenta) both let it continue.
Level 1 — Recognition
Recall Solution L1-1
- Slot 0:
_→ EMPTY - Slot 1:
_→ EMPTY - Slot 2:
A→ OCCUPIED - Slot 3:
†→ DELETED (tombstone) - Slot 4:
C→ OCCUPIED - Slot 5:
_→ EMPTY - Slot 6:
_→ EMPTY
Recall Solution L1-2
Only EMPTY stops an unsuccessful search. OCCUPIED (wrong key) and DELETED both mean keep probing. Search also stops — successfully — when it lands on the key itself.
Level 2 — Application
Recall Solution L2-1
All three hash to , , — same home slot 3.
- Insert 10: slot 3 free →
[_ _ _ 10 _ _ _] - Insert 17: slot 3 taken → probe 4 (free) →
[_ _ _ 10 17 _ _] - Insert 24: slot 3, 4 taken → probe 5 (free) →
[_ _ _ 10 17 24 _] - Delete 17: locate at slot 4, set DELETED →
[_ _ _ 10 † 24 _]
Figure s02 animates this exact build as four stacked snapshots — watch the chain grow at slots 3→4→5, then slot 4 flip to a magenta tombstone in the last row.

† (not EMPTY).
Recall Solution L2-2
. Probe slot 3 (=10, not it) → slot 4 (=†, keep going) → slot 5 (=24 ✔). 3 probes. If slot 4 had been blanked to EMPTY, search would stop at slot 4 (2 probes) and wrongly report 24 missing.
Figure s03 contrasts the two searches side by side: the tombstone chain (top) walks through to 24; the wrongly-EMPTY chain (bottom) dead-ends at slot 4.
Figure s03 — Why the tombstone matters: with † the search reaches 24 (green ✔); with _ it stops early and fails (red ✗).
Recall Solution L2-3
. Probe slot 3 (10, no) → 4 (†, keep going) → 5 (24, no) → 6 (_ EMPTY → STOP).
4 probes, then correctly reports absent. The tombstone forced one extra probe compared with a clean table — this is the tombstone's cost in action.
Level 3 — Analysis
Recall Solution L3-1
. Insert scans the probe chain, remembering the first tombstone but not stopping there:
- slot 3: OCCUPIED (10) → not 45, remember nothing yet, continue
- slot 4:
†→ remember "first tombstone = slot 4", continue (must check for a duplicate 45 further on) - slot 5: OCCUPIED (24) → not 45, continue
- slot 6:
_EMPTY → chain ends, no duplicate 45 exists.
Now place 45 at the remembered tombstone, slot 4: [_ _ _ 10 45 24 _].
Insert reused the grave and shortened future chains — the tombstone paid off.
Recall Solution L3-2
Wrong (stop-early) path: insert sees † at slot 4 and drops 24 there → [_ _ _ 10 24 24 _]. Now two copies of 24 exist. A later search finds the slot-4 copy; delete it, and the slot-5 copy is orphaned — corruption.
Correct path: scan past the tombstone: slot 4 (†, remember) → slot 5 (=24, key already present!) → stop, do not insert a duplicate (update in place if needed). No duplicate created.
This is exactly why insert must scan to EMPTY-or-key even after finding a reusable tombstone.
Recall Solution L3-3
. .
With tombstones (): After purging tombstones (): The tombstones nearly quadruple the search cost while only 3 real keys live in a size-10 table. This is the concrete reason to rehash.
Level 3½ — Edge case: wrap-around at the end of the array
Recall Solution L3-4
. Probe starts at the last slot:
- : index → OCCUPIED (24), continue.
- : index → the
\bmod mwraps around to slot 0, which is EMPTY.
Place 9 at slot 0: [9 _ _ 20 24]. The mod m in is exactly what makes probing circular — running off index 4 loops back to index 0, so the array behaves like a ring, never a dead-end. Every search and insert in the earlier exercises silently relied on this; here we saw it fire.
Level 4 — Synthesis
Recall Solution L4-1
(a) Before the delete. Count occupied plus tombstones over : Already — the table was over threshold before we even touched it.
(b) After the delete. A delete converts one OCCUPIED slot into a tombstone: drops from 4 to 3, rises from 2 to 3. The sum is unchanged: Deletion never lowers — it just swaps one counted state for another.
(c) Verdict. , so the policy triggers a rehash. Rehashing reinserts only the 3 live keys into a fresh array (the 3 tombstones are dropped). Keeping , the reset value is Rehash is the only operation that actually shrinks , because it is the only one that removes tombstones from the count.
Recall Solution L4-2
All of 5,10,15,20 hash to (since each is a multiple of 5). Chain builds at slots 0,1,2,...
- insert 5 → slot 0:
[5 _ _ _ _] - insert 10 → slot 0 taken → slot 1:
[5 10 _ _ _] - insert 15 → 0,1 taken → slot 2:
[5 10 15 _ _] - delete 10 → slot 1 becomes
†:[5 † 15 _ _] - insert 20 → probe slot 0 (5, no), slot 1 (†, remember), slot 2 (15, no), slot 3 (
_EMPTY, chain ends, no duplicate 20) → place at remembered tombstone slot 1:[5 20 15 _ _] - search 15 → slot 0 (5, no) → slot 1 (20, no) → slot 2 (15 ✔): 3 probes.
Insert step 5 reused the grave, so after it there are no tombstones left and the chain stays tight.
Level 5 — Mastery
Recall Solution L5-1
Invariant. If key is present at position , then for every , the slot is not EMPTY (it is OCCUPIED or DELETED). Why deletion-to-tombstone preserves it. Consider deleting some key that sat at a slot on 's probe chain, i.e. for some . Before deletion, slot was OCCUPIED, so the invariant held.
- If we set to EMPTY, then becomes EMPTY with — the invariant is violated, and a search for stops at slot , failing to reach position .
- If we set to DELETED, then is still "not EMPTY" — the invariant still holds. Search treats DELETED like OCCUPIED, walks past , and reaches at position . ∎
The tombstone is precisely the minimal change that keeps "not EMPTY" true for every intermediate slot without pretending a live key is present.
Recall Solution L5-2
Separate chaining: each bucket is a linked list; deleting a key just unlinks a node — O(1), and it genuinely removes the entry with no side effect on any other key's lookup (each key's chain is independent of others). There is no shared probe path to corrupt, so no tombstone concept exists and never gets inflated by deletions. Open addressing: all keys share one array and probe paths overlap; a blanked slot corrupts every chain threading through it. Tombstones repair this but inflate , so search slows and periodic rehashing is mandatory. When open addressing still wins: with cache locality and low load factor and rare deletions. Contiguous array probing hits far fewer cache lines than pointer-chasing a linked list. If the workload is read-heavy with occasional deletes (tombstones rare, rehash rare), open addressing's fast searches and compact memory outperform chaining's pointer overhead — the O(1) delete of chaining doesn't matter if you delete seldom.
Recall Solution L5-3
What a rehash does, entry by entry. It scans all slots of the old array. For each slot it looks at the state:
- EMPTY → skip (nothing to move).
- DELETED (
†) → skip and drop — tombstones are not carried over. - OCCUPIED → reinsert the live key into the fresh array.
So of the up-to- counted entries, only the live (OCCUPIED) ones are reinserted; every tombstone evaporates. Because the trigger fired at , the number of live keys is at most (fewer if some of the count was tombstones).
Why the new . Those live keys go into an array of the doubled size , and no tombstones exist right after a rebuild, so
The amortised argument. A rehash costs O(m): scan slots + reinsert keys. To climb from the post-rebuild value () back up to the trigger () at the doubled size , you must perform at least counted operations (each insert or delete bumps by one). Spreading the O(m) rebuild over those operations gives Each individual insert/search/delete is O(1) expected at bounded load factor, so the amortised per-operation cost is O(1) — the same "charge the rebuild to the operations that filled the table" accounting used for dynamic array doubling. ∎
80/20 recap of this exercise set
- Read states (L1): EMPTY ends,
†and OCCUPIED continue. - Trace inserts/deletes/searches (L2–L3): deletion writes
†; insert remembers first†but scans to EMPTY-or-key. - Wrap-around (L3½): probing is circular via
mod m; the last slot loops back to slot 0. - Cost is -driven (L3): tombstones inflate search even with few live keys.
- Policy design (L4–L5): rehash on an threshold; amortised O(1); tombstones prove the search invariant survives deletion.
Connections
- Deletion in open addressing — tombstone markers (index 3.3.5) — the parent note these exercises drill.
- Open Addressing — the framework all problems live in.
- Linear Probing — the probe rule used in every trace.
- Load Factor and Rehashing — the threshold and doubling of L3–L5.
- Separate Chaining — the O(1)-delete contrast of L5-2.
- Hash Functions — the that seeds every chain.