Problem Walkthrough

Stock Price Fluctuation LeetCode 2034

Use a HashMap to track the current price at each timestamp, plus a max-heap and min-heap with lazy deletion so update(), current(), maximum(), and minimum() all run in O(log n) amortized time.

9 min read|

Stock Price Fluctuation

LeetCode 2034

Problem Overview

LeetCode 2034 asks you to implement a StockPrice class that processes a stream of stock price records. Each record is a (timestamp, price) pair. The catch: updates can correct previously seen timestamps, meaning the price at a given timestamp can change over time.

Your class must support four operations efficiently. First, update(timestamp, price) records the latest price for a given timestamp. Second, current() returns the price at the most recent (highest) timestamp seen so far. Third, maximum() returns the highest price among all current prices across all timestamps. Fourth, minimum() returns the lowest price across all timestamps.

The constraints allow up to 100,000 calls, so naive O(n) scans for max and min will time out. You need a design that handles corrections to past prices without blowing up the time complexity.

  • update(timestamp, price) — record or correct the price at a given timestamp
  • current() — return the price at the latest (maximum) timestamp seen
  • maximum() — return the globally highest price across all current timestamps
  • minimum() — return the globally lowest price across all current timestamps
  • Updates can correct a previous timestamp — price at a timestamp can change

Why Simple Max/Min Fails

A naive approach might keep a running max and min. On each update, if the new price is larger than the current max, update the max — similarly for min. This works fine as long as prices only arrive in order and are never corrected.

The problem is corrections. Suppose timestamp 5 was recorded with price 100, making it the global maximum. Then a correction arrives: timestamp 5 is now price 30. Your running max is stale — 100 is no longer a valid price for any timestamp, but you have no efficient way to know what the new maximum should be without scanning all stored prices.

This invalidation problem rules out any approach that only maintains a single running value. You need a structure that lets you query the true current maximum and minimum even after arbitrary corrections to past timestamps.

💡

Key Insight

The correction aspect is what makes this hard: a timestamp's price can change, invalidating previous max/min values. You need a structure that handles updates efficiently while still answering max/min queries correctly after any past timestamp is corrected.

HashMap + Two Heaps

The standard solution combines three data structures. A HashMap (prices) maps each timestamp to its current price. A max-heap stores (price, timestamp) pairs and lets you query the highest price in O(log n). A min-heap stores the same pairs in reverse order and lets you query the lowest price in O(log n).

On each update call, you store the new (timestamp, price) in the HashMap and push the pair into both heaps. You do not remove the old price from the heaps — that would require O(n) time for an arbitrary heap. Instead you use lazy deletion.

When querying maximum() or minimum(), you peek at the heap top. If the price stored in the heap matches what the HashMap says is the current price for that timestamp, the entry is valid. If it does not match, the timestamp has been corrected and that heap entry is stale — pop it and check the next one.

  1. 1Store timestamp → price in a HashMap on every update call
  2. 2Push (price, timestamp) into the max-heap and (-price, timestamp) into the min-heap
  3. 3On current(): return prices[maxTimestamp] where maxTimestamp tracks the largest timestamp seen
  4. 4On maximum(): peek at the max-heap top; if prices[timestamp] != heap price, pop it as stale; repeat until valid
  5. 5On minimum(): same lazy deletion process on the min-heap

Lazy Deletion Explained

Lazy deletion is a technique where you avoid the expensive work of removing an element from a heap when it becomes invalid. Instead, you leave stale entries in place and only discard them when they surface at the top of the heap during a query.

In this problem, every time a timestamp is corrected, the old (price, timestamp) pair in the heap becomes stale. Rather than hunting it down in O(n) and re-heapifying, you simply push the new entry and move on. The stale entry remains buried somewhere in the heap.

When you call maximum() or minimum(), you check if the heap top is consistent with the HashMap. If prices[timestamp] differs from what is stored in the heap, that entry is from an old correction and you pop it. You keep popping until you find an entry that matches — and that is your true current max or min.

ℹ️

Amortized Analysis

Lazy deletion works because each stale entry is popped at most once. Total deletions across all operations cannot exceed the total number of updates (since each update creates at most one stale entry). This gives O(log n) amortized time per query, despite occasional bursts of multiple pops.

SortedList Alternative

Python's SortedList from the sortedcontainers library provides a cleaner solution. A SortedList maintains all elements in sorted order and supports O(log n) add, remove, min, and max. It is essentially a balanced BST with list-like indexing.

On each update call, if the timestamp already exists in the HashMap, you remove the old price from the SortedList before inserting the new one. Then update the HashMap. This way, the SortedList always contains exactly the current prices — no stale entries, no lazy deletion needed.

The downside is that sortedcontainers is a third-party library not available in all interview environments or languages. In Java you would use a TreeMap with price → count of timestamps at that price, which handles duplicates properly. The heap approach is more portable and is the idiomatic Python solution in interviews.

  1. 1Maintain a SortedList of all current prices and a HashMap of timestamp → price
  2. 2On update: if timestamp exists in HashMap, remove old price from SortedList
  3. 3Add new price to SortedList and update HashMap
  4. 4current() returns prices[maxTimestamp] as before
  5. 5maximum() returns sortedList[-1] and minimum() returns sortedList[0] — O(1) lookup

Code Walkthrough: Python and Java

In Python, the max-heap is simulated by pushing negative prices (since Python's heapq is a min-heap). Push (-price, timestamp) for the max-heap and (price, timestamp) for the min-heap. Track maxTimestamp as a plain integer updated on each update() call. The current() method simply returns self.prices[self.maxTimestamp].

For maximum(): while the heap top's price does not match self.prices[top_timestamp], pop. The price stored in the heap is negative for the max-heap, so compare -heap[0][0] against self.prices[heap[0][1]]. Return -heap[0][0] when they agree. The minimum() implementation mirrors this using the min-heap directly.

In Java, use two PriorityQueues. The max-heap uses a comparator that reverses natural order. Use a TreeMap<Integer, Integer> (timestamp → price) as the HashMap and track the last key via treeMap.lastKey() for current(). On each maximum()/minimum() call, poll the heap while the polled price does not match the map value for that timestamp.

⚠️

Common Mistake

Track the maximum timestamp separately for current(): do not scan all timestamps. On each update() call, run maxTimestamp = max(maxTimestamp, timestamp). This keeps current() at O(1). Scanning the entire HashMap on each current() call turns a simple getter into an O(n) bottleneck.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now