Jukebox That Plays the Most-Requested Song First
AmazonSoftware Engineer · OnsiteLast updated
Design a request queue where each play takes the song with the most pending requests, with ties going to the most recent request. Tests keeping frequency and recency together so both operations run in O(1).
A bar's jukebox takes song requests from customers' phones. When the current song ends, the jukebox picks the next song this way:
- Choose the song with the most pending requests.
- If several songs tie, choose the one whose latest pending request is the most recent.
- Playing a song uses up only one request for it, the most recent one. The rest stay pending.
Implement the class:
class Jukebox:
def request(self, song: str) -> None: ...
def play_next(self) -> str: ... # never called when nothing is pending
Also write a small driver, run(log: list[str]) -> list[str]. Each entry in log is either a song name (a request) or the literal string "PLAY". The driver returns the songs played, in order.
Examples
log = ["lofi", "jazz", "lofi", "rock", "jazz",
"PLAY", "PLAY", "PLAY", "PLAY", "PLAY"]
out = ["jazz", "lofi", "rock", "jazz", "lofi"]
- Pending: lofi x2, jazz x2, rock x1. lofi and jazz tie at 2, and jazz's latest request (5th) is newer than lofi's (3rd). Play jazz.
- Now lofi is the only song with 2. Play lofi.
- Every song now has 1 pending request. The newest still-pending one is rock (4th). Play rock. Then jazz (2nd), then lofi (1st).
log = ["a", "b", "b", "PLAY", "a", "PLAY", "PLAY", "c", "PLAY"]
out = ["b", "a", "b", "c"]
Constraints
- Up to
2 * 10^5operations in total. play_nextandrequestshould each run in O(1) time (amortized is fine).- Song names are arbitrary non-empty strings, never equal to
"PLAY".
Hints
Hint 1
A heap keyed on (count, last-request time) works in O(log n), but stale entries make it messy. Can you avoid ordering entirely?
Hint 2
Counts only ever change by +1 or -1. Think about grouping songs by how many pending requests they have.
Hint 3
Keep one stack per count level. When a song reaches count k, push it onto stack k. The top of the highest non-empty stack is always the answer.
Hint 4
When the highest stack becomes empty, the new maximum is exactly one lower. Why?
Follow-up questions
- Add `cancel(song)`, which withdraws a customer's most recent request for that song. Can you keep O(1)?
- Ties should now go to the song requested **earliest**. What changes?
- Requests expire after 30 minutes. How do you handle expiry without scanning everything?
- How would you shard this across many venues served by one backend?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.