Quick Overview

Implement a lazy chronological merge iterator with one head per active stream, stable timestamp ties, has-next semantics, exhausted streams, and bounded auxiliary memory.

Merge Sorted Event Streams Through an Iterator

Company: Amazon

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

Implement an iterator that merges several event streams already sorted by timestamp. Return events chronologically without collecting and sorting every event before iteration. Implement `merge_event_iterator(streams: string[][], operations: string[]) -> string[]`. ### Constraints & Assumptions The nested arrays provide deterministic stream fixtures for practice. Your iterator may keep an index or iterator for each stream and at most one pending head per active stream; do not flatten or copy all events into a new collection. - Each event string is `timestamp/payload`, with a nonnegative integer timestamp at most 1000000000 and a nonempty ASCII alphanumeric/underscore payload. Payloads need not be unique. - Every input stream is sorted nondecreasingly by numeric timestamp. There may be empty streams, no streams, and identical timestamps. - Ties use lower stream index first, preserving original order within a stream. This deterministic tie rule is a practice assumption; the source asks to handle identical timestamps without prescribing their order. - At most 10000 streams, 200000 total events, and 300000 operations. - `HAS_NEXT` returns `true` or `false` as a string without advancing. `NEXT` returns the next original event string, or `END` if exhausted. Repeated NEXT after exhaustion keeps returning END. - Return one string for every operation. Output storage is excluded from the iterator's auxiliary-memory bound. ### Example ```text streams = [["1/a","3/b"],[],["1/c","2/d"]] operations = ["HAS_NEXT","NEXT","NEXT","NEXT","NEXT","HAS_NEXT","NEXT"] result = ["true","1/a","1/c","2/d","3/b","false","END"] ``` Explain initialization, advancement, equal timestamps, and exhausted streams. Analyze time per returned event and memory in terms of the number of streams. Discuss what changes when stream heads are obtained from real blocking or asynchronous sources rather than already available fixtures. ```hint Only the current heads can be next After emitting one stream's head, only that stream needs to supply a replacement candidate. ```

Overview: Implement a lazy chronological merge iterator with one head per active stream, stable timestamp ties, has-next semantics, exhausted streams, and bounded auxiliary memory.

Read the full Amazon Software Engineer interview experience this question came from

Implement an iterator that merges several event streams already sorted by timestamp. Return events chronologically without collecting and sorting every event before iteration. Implement `merge_event_iterator(streams: string[][], operations: string[]) -> string[]`. ### Constraints & Assumptions The nested arrays provide deterministic stream fixtures for practice. Your iterator may keep an index or iterator for each stream and at most one pending head per active stream; do not flatten or copy all events into a new collection. - Each event string is `timestamp/payload`, with a nonnegative integer timestamp at most 1000000000 and a nonempty ASCII alphanumeric/underscore payload. Payloads need not be unique. - Every input stream is sorted nondecreasingly by numeric timestamp. There may be empty streams, no streams, and identical timestamps. - Ties use lower stream index first, preserving original order within a stream. This deterministic tie rule is a practice assumption; the source asks to handle identical timestamps without prescribing their order. - At most 10000 streams, 200000 total events, and 300000 operations. - `HAS_NEXT` returns `true` or `false` as a string without advancing. `NEXT` returns the next original event string, or `END` if exhausted. Repeated NEXT after exhaustion keeps returning END. - Return one string for every operation. Output storage is excluded from the iterator's auxiliary-memory bound. ### Example ```text streams = [["1/a","3/b"],[],["1/c","2/d"]] operations = ["HAS_NEXT","NEXT","NEXT","NEXT","NEXT","HAS_NEXT","NEXT"] result = ["true","1/a","1/c","2/d","3/b","false","END"] ``` Explain initialization, advancement, equal timestamps, and exhausted streams. Analyze time per returned event and memory in terms of the number of streams. Discuss what changes when stream heads are obtained from real blocking or asynchronous sources rather than already available fixtures. ```hint Only the current heads can be next After emitting one stream's head, only that stream needs to supply a replacement candidate. ```

Constraints

  • At most 10000 streams, 200000 total events and 300000 operations; empty streams and no streams are valid.
  • Events are timestamp/payload with nonnegative timestamp at most 1000000000 and nonempty ASCII alphanumeric/underscore payload.
  • Each stream is sorted nondecreasingly by numeric timestamp; ties use lower stream index and preserve within-stream order.
  • HAS_NEXT returns true or false strings without advancing; NEXT returns an original event string or END repeatedly after exhaustion.
  • Keep at most one pending head per active stream and per-stream positions or iterators; do not flatten or copy all events.

Examples

Input: ([['1/a', '3/b'], [], ['1/c', '2/d']], ['HAS_NEXT', 'NEXT', 'NEXT', 'NEXT', 'NEXT', 'HAS_NEXT', 'NEXT'])

Expected Output: ['true', '1/a', '1/c', '2/d', '3/b', 'false', 'END']

Explanation: The source example merges current heads and detects exhaustion.

Input: ([['1/a', '1/b'], ['1/c', '2/d'], ['1/e']], ['NEXT', 'NEXT', 'NEXT', 'NEXT', 'NEXT', 'NEXT'])

Expected Output: ['1/a', '1/b', '1/c', '1/e', '2/d', 'END']

Explanation: Equal timestamps always prefer the lower stream, including its replacement head.

Community answers

Answer by janaki9sravya

import heapq def merge_event_iterator(streams, operations): n = len(streams) s_ptrs = [0] * n min_heap = [] def push_head(i): if s_ptrs[i] < len(streams[i]): k, v = streams[i][s_ptrs[i]].split("/") heapq.heappush(min_heap, (int(k), v, i)) s_ptrs[i] += 1 for i in range(n): push_head(i) result = [] for op in operations: if op == "HAS_NEXT": result.append("true" if min_heap else "false") else: if not min_heap: result.append("END") continue k, v, i = heapq.heappop(min_heap) result.append(f"{k}/{v}") push_head(i) # refill THIS stream right after popping it return result

Loading coding console...

Show the approach

Approach

Keep a min-heap of triples (numeric timestamp, stream index, position), initially containing only the first event of each nonempty stream. HAS_NEXT checks whether the heap is empty and never modifies it. NEXT removes the smallest head, returns the original string at its stored position, and inserts only that stream's next event if present. Sorted input guarantees every unseen event follows its stream head, so the global next event must be among these heads. Comparing timestamp then stream index implements ties; only one head from a stream exists at a time, preserving its internal order even through repeated equal timestamps. Empty and exhausted streams are absent, and an empty heap always yields END for NEXT. No flattened event collection is built or sorted. With k streams, initialization is O(k log(k+1)) as an upper bound across languages, or O(k) with Python heapify; a returned event takes O(log(k+1)) heap work plus timestamp parsing and output string costs. HAS_NEXT and exhausted NEXT take O(1). Iterator state has O(k) entries excluding output and input fixtures; C++ takes fixtures by const reference. For real blocking/asynchronous streams, obtaining each initial or replacement head may require waiting. Correct global ordering requires a head, end-of-stream marker, or a trustworthy watermark from every relevant source; emitting whichever response arrives first can violate timestamp or tie order. Cancellation, backpressure, errors and delayed sources need an explicit interface policy.

Time complexity:
Initialization O(k log(k+1)); returned NEXT O(log(k+1)); HAS_NEXT/exhausted NEXT O(1), plus timestamp and output string costs
Space complexity:
O(k) iterator entries, excluding fixtures and returned output