Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 13 – Heaps and top-K

Heaps with lazy deletion

Keep the highest and lowest current price while prices change: the problem, a scan that reads 4 × 10¹⁰ prices, and why old heap entries can wait.

  • Advanced
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 7 of 35 minutes free
Free: The problem: the highest and lowest price on a changing board · The brute force, and why it fails at these sizes · What to notice. With a pass: the other 13 sections and 3 interview answers, about 28 minutes.

What you will learn

  • Update or remove heap entries by marking them stale and dropping them only when they reach the top
  • Distinguish stale entries from current ones, even when a value returns to an earlier one
  • Track the highest and lowest current price of keys whose prices keep changing
  • Limit the memory that stale entries hold without raising the cost per update
  • Compare lazy deletion with ordered maps and indexed heaps by cost, memory and code

Before you start

What you’ll learn · Before you start

What you will learn

  • Update or remove heap entries by marking them stale and dropping them only when they reach the top
  • Distinguish stale entries from current ones, even when a value returns to an earlier one
  • Track the highest and lowest current price of keys whose prices keep changing
  • Limit the memory that stale entries hold without raising the cost per update
  • Compare lazy deletion with ordered maps and indexed heaps by cost, memory and code

Before you start

On this page

A heap cannot remove an arbitrary entry cheaply: it knows where its top is and nothing else, so finding an old entry takes O(n). Lazy deletion skips the search. When a value changes, push a new entry, leave the old one where it is, and record elsewhere which entry is current. An old entry can only affect an answer once it reaches the top, so a query pops entries off the top while they are out of date and then answers. Each entry is pushed once and popped at most once, which keeps every operation at O(log n), amortised. A price board shows the problem, its brute force and the signal.

The problem: the highest and lowest price on a changing board

The problem

A price board lists items with their current prices. It receives n updates, each (item, price): from now on the item costs that price, whether it is new on the board or already listed. After every update, report the highest and the lowest current price on the board. Limits: up to 200,000 updates and as many different items, with prices from 1 to 10⁹.

The updates tea 120, rice 90, tea 80, oil 150 and oil 70 give (120, 120), (120, 90), (90, 80), (150, 80) and (90, 70), as (highest, lowest). After tea drops to 80, its old price of 120 no longer counts, so the highest is rice at 90.

The brute force, and why it fails at these sizes

Keep each item’s current price in a dictionary, and after every update read every price to find the largest and the smallest. Each update costs time in proportion to the number of items on the board.

Every price read after every update Python · brute/brute.py
# The brute force: keep each item's current price in a dict, and scan every price after every update.


def board_by_scanning(updates):
    prices, out, looks = {}, [], 0
    for item, price in updates:
        prices[item] = price  # a new item, or a corrected price
        looks += 2 * len(prices)  # max() and min() each read every price
        out.append((max(prices.values()), min(prices.values())))
    return out, looks


updates = [("tea", 120), ("rice", 90), ("tea", 80), ("oil", 150), ("oil", 70)]
answers, looks = board_by_scanning(updates)
print(" ".join(f"({hi}, {lo})" for hi, lo in answers), f"[{looks} prices read]")

n = 200_000
print(f"at the limit, {n:,} updates of {n:,} different items: about n * n = {n * n:,} prices read")

Output

(120, 120) (120, 90) (90, 80) (150, 80) (90, 70) [22 prices read]
at the limit, 200,000 updates of 200,000 different items: about n * n = 40,000,000,000 prices read

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute.py

With 200,000 updates that each add a new item, the board grows to 200,000 items, and the two scans read about 4 × 10¹⁰ prices in all. A heap gives the largest or the smallest in O(log n), but an update must also get rid of the item’s old entry, and a heap can find that entry only by searching all of it.

What to notice

Current prices tea 80, rice 90, oil 70; a min-heap holds 70 oil on top, 80 tea, 90 rice and two out-of-date entries, 120 tea and 150 oil.Current prices: tea 80, rice 90, oil 70Min-heap entries, smallest firstlowest() compares only the top with the current prices:70 is oil's price, so 70 is the answer70 oil: current, on top80 tea: current90 rice: current120 tea: out of date150 oil: out of dateevery update adds an entry;none is searched for or removed

Out-of-date entries stay in the heap

Text description of the diagram

At the top, the current prices after five updates: tea 80, rice 90 and oil 70.

An arrow labelled "every update adds an entry; none is searched for or removed" leads to the min-heap's entries, listed from the smallest: 70 oil, which is current and on top, drawn with a double border; 80 tea, current; 90 rice, current; 120 tea, out of date, drawn dashed, because tea's price is now 80; and 150 oil, out of date, drawn dashed, because oil's price is now 70.

An arrow leads to the query: lowest() compares only the top entry with the current prices. 70 is oil's current price, so 70 is the answer, and the two out-of-date entries stay where they are until one of them reaches the top.

  • A query reads only the top of a heap. An out-of-date entry anywhere below the top cannot change the answer.
  • An out-of-date entry is easy to recognise when it does reach the top, by comparing it with the item’s current record.

So an update never has to search: it pushes the new entry and leaves the old one in place. The search happens only at the top, one entry at a time, and each out-of-date entry is thrown away once.

Lazy deletion, step by step: versions and a check at the top Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Why every answer is right: the invariant
  • A dry run on the price board
  • The cost, amortised, and the memory that stale entries hold
  • Measured: stale entries with and without rebuilding
  • Lazy deletion, an ordered map or an indexed heap
  • The code in five languages
  • Edge cases and common bugs, each found by a drill
  • Variants: removal, the best k players, corrected readings and Dijkstra
  • Exercise: a price book with the highest and lowest current price
    • A price book with the highest and lowest current price
  • A practice set with sample answers
  • Check yourself
  • Interview questions
    • Why does lazy deletion leave old entries in a heap instead of removing them when a value changes?
    • A leaderboard keeps (score, player) entries in a max-heap and treats an entry as current when its score equals the player's current score. It passes most tests, but sometimes lists the same player twice in the top three. Why, and what do you change?
    • A service tracks the lowest price of 10,000 products with a min-heap and lazy deletion. Prices fall slowly all day, and by evening the process uses gigabytes of memory, although only 10,000 prices are current. Explain the growth and fix it without hurting the cost per update.
    • Design a game leaderboard for a million players with three operations: set a player's score, return the top 10, and return one player's rank. Scores change thousands of times a second. Where does lazy deletion fit, and where does it not?
  • Key takeaways

6 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.