Quick Overview

Merge K price-event feeds, each already sorted by timestamp, into one chronological stream and return the running absolute price after every event, starting from zero. It tests merging sorted sequences efficiently, a deterministic tie-break for equal timestamps by feed index and then in-feed order, negative deltas, empty feeds, and 64-bit accumulation.

Merge K Timestamp-Sorted Price Feeds into a Running Absolute Price

Company: Citadel

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

You are given `K` data feeds of price events. Each event is a pair `[timestamp, delta]`: the time at which the event occurred and the change in price it carries. Within each feed, events are already listed in non-decreasing timestamp order. A feed's index is its position in the input list, from `0` to `K - 1`. Process the events from all feeds as one chronological stream. The absolute price starts at `0`, and each event adds its `delta` to it. Return the absolute price recorded after each event, in processing order. ### Function Signature ```python def running_prices(feeds: list[list[list[int]]]) -> list[int]: ``` ### Rules - **Processing order.** An event with a smaller timestamp is processed before an event with a larger one. When two events share a timestamp and come from different feeds, the event from the feed with the smaller index is processed first. When two events share a timestamp and come from the same feed, they are processed in the order they appear in that feed. Equivalently, events are processed in ascending order of the key `(timestamp, feed index, position within its feed)`. - **Running price.** The price starts at `0`. Processing an event sets `price = price + delta`, and the new price is appended to the output, so the output holds exactly one value per event. - **Negative values.** Deltas may be negative, and the running price may drop below zero. It is never clamped. - **Empty feeds.** A feed may contain no events, in which case it contributes nothing. If there are no events at all, including when `feeds` itself is empty, return an empty list. ### Constraints - `0 <= K = len(feeds) <= 10000` - `0 <= N <= 100000`, where `N` is the total number of events across all feeds - Every event is a list of exactly two integers, `[timestamp, delta]`. - `0 <= timestamp <= 1000000000` - `-1000000 <= delta <= 1000000` - Within each feed, timestamps are non-decreasing, so equal timestamps may repeat inside one feed. Different feeds have no ordering relationship with each other. - Every running price lies between `-100000000000` and `100000000000` inclusive. This exceeds the 32-bit signed integer range (`2^31 - 1`), so use 64-bit integers in fixed-width languages. - The output is a list of length `N`, and it is uniquely determined by the input. ### Examples **Example 1** - Input: `feeds = [[[1, 5], [4, -2]], [[2, 3], [3, -1]]]` - Output: `[5, 8, 7, 5]` - Explanation: The processing order is `[1, 5]` from feed 0, `[2, 3]` from feed 1, `[3, -1]` from feed 1, then `[4, -2]` from feed 0. Starting from 0, the price becomes 5, 8, 7 and finally 5. **Example 2** - Input: `feeds = [[[2, -4], [2, 1]], [], [[1, 10], [2, 7]]]` - Output: `[10, 6, 7, 14]` - Explanation: Feed 1 is empty. The only event at timestamp 1 is `[1, 10]` from feed 2, so it is processed first and the price becomes 10. Three events share timestamp 2. The two from feed 0 come first, in their order within that feed (`-4` and then `1`, giving 6 and then 7), followed by `[2, 7]` from feed 2, giving 14. **Example 3** - Input: `feeds = [[[3, -2]], [[1, -5], [3, 4]]]` - Output: `[-5, -7, -3]` - Explanation: `[1, -5]` from feed 1 is processed first, so the price becomes -5. At timestamp 3, the event from feed 0 precedes the event from feed 1, so the price moves to -7 and then to -3. The price stays negative throughout, which is allowed.

Overview: Merge K price-event feeds, each already sorted by timestamp, into one chronological stream and return the running absolute price after every event, starting from zero. It tests merging sorted sequences efficiently, a deterministic tie-break for equal timestamps by feed index and then in-feed order, negative deltas, empty feeds, and 64-bit accumulation.

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

You are given `K` data feeds of price events. Each event is a pair `[timestamp, delta]`: the time at which the event occurred and the change in price it carries. Within each feed, events are already listed in non-decreasing timestamp order. A feed's index is its position in the input list, from `0` to `K - 1`. Process the events from all feeds as one chronological stream. The absolute price starts at `0`, and each event adds its `delta` to it. Return the absolute price recorded after each event, in processing order. Implement `running_prices(feeds)`. ### Rules - **Processing order.** An event with a smaller timestamp is processed before an event with a larger one. When two events share a timestamp and come from different feeds, the event from the feed with the smaller index is processed first. When two events share a timestamp and come from the same feed, they are processed in the order they appear in that feed. Equivalently, events are processed in ascending order of the key `(timestamp, feed index, position within its feed)`. - **Running price.** The price starts at `0`. Processing an event sets `price = price + delta`, and the new price is appended to the output, so the output holds exactly one value per event. - **Negative values.** Deltas may be negative, and the running price may drop below zero. It is never clamped. - **Empty feeds.** A feed may contain no events, in which case it contributes nothing. If there are no events at all, including when `feeds` itself is empty, return an empty list. ### Constraints - `0 <= K = len(feeds) <= 10000` - `0 <= N <= 100000`, where `N` is the total number of events across all feeds - Every event is a list of exactly two integers, `[timestamp, delta]`. - `0 <= timestamp <= 1000000000` - `-1000000 <= delta <= 1000000` - Within each feed, timestamps are non-decreasing, so equal timestamps may repeat inside one feed. Different feeds have no ordering relationship with each other. - Every running price lies between `-100000000000` and `100000000000` inclusive. This exceeds the 32-bit signed integer range (`2^31 - 1`), so use 64-bit integers in fixed-width languages: Java returns `long[]` and C++ returns `std::vector<long long>`. Every running price stays well below `2^53`, so JavaScript numbers hold it exactly. - The output is a list of length `N`, and it is uniquely determined by the input. ### Examples **Example 1** ```text Input: feeds = [[[1, 5], [4, -2]], [[2, 3], [3, -1]]] Output: [5, 8, 7, 5] ``` The processing order is `[1, 5]` from feed 0, `[2, 3]` from feed 1, `[3, -1]` from feed 1, then `[4, -2]` from feed 0. Starting from 0, the price becomes 5, 8, 7 and finally 5. **Example 2** ```text Input: feeds = [[[2, -4], [2, 1]], [], [[1, 10], [2, 7]]] Output: [10, 6, 7, 14] ``` Feed 1 is empty. The only event at timestamp 1 is `[1, 10]` from feed 2, so it is processed first and the price becomes 10. Three events share timestamp 2. The two from feed 0 come first, in their order within that feed (`-4` and then `1`, giving 6 and then 7), followed by `[2, 7]` from feed 2, giving 14. **Example 3** ```text Input: feeds = [[[3, -2]], [[1, -5], [3, 4]]] Output: [-5, -7, -3] ``` `[1, -5]` from feed 1 is processed first, so the price becomes -5. At timestamp 3, the event from feed 0 precedes the event from feed 1, so the price moves to -7 and then to -3. The price stays negative throughout, which is allowed.

Constraints

  • 0 <= K = len(feeds) <= 10000
  • 0 <= N <= 100000, where N is the total number of events across all feeds
  • Every event is a list of exactly two integers, [timestamp, delta]
  • 0 <= timestamp <= 1000000000
  • -1000000 <= delta <= 1000000
  • Within each feed, timestamps are non-decreasing (equal timestamps may repeat inside one feed); different feeds have no ordering relationship with each other
  • Every running price lies between -100000000000 and 100000000000 inclusive, which exceeds 2^31 - 1; use 64-bit integers (Java long, C++ long long)
  • The output is a list of length N and is uniquely determined by the input

Examples

Input: ([[[1, 5], [4, -2]], [[2, 3], [3, -1]]],)

Expected Output: [5, 8, 7, 5]

Explanation: Example 1: two feeds interleave by timestamp.

Input: ([[[2, -4], [2, 1]], [], [[1, 10], [2, 7]]],)

Expected Output: [10, 6, 7, 14]

Explanation: Example 2: an empty feed, and a timestamp shared inside one feed and across feeds.

Hints

  1. Each feed is already sorted by timestamp. At any moment, which events are the only candidates to be processed next?
  2. Keep one current event per non-empty feed in a structure that can repeatedly hand you the smallest one, and decide what key makes equal timestamps come out in the required order.
  3. A running price can reach 10^11 in magnitude, so check the width of your accumulator and your return type.

Loading coding console...

Show the approach

Approach

This is a k-way merge of already-sorted lists. Because each feed is sorted by timestamp, the next event in the global order must be the first unprocessed event of some feed, so only K candidates ever matter at once.

Algorithm: put the first event of every non-empty feed into a min-heap keyed by (timestamp, feed index), remembering its position inside the feed. Repeatedly pop the minimum, add its delta to the running price (starting from 0), append the new price to the answer, and push the next event of the same feed if there is one.

Why the order is right: each feed has at most one entry in the heap at a time, so the pair (timestamp, feed index) is unique inside the heap and no further tie-breaking is needed. Events of one feed enter the heap one after another, so events that share a timestamp inside a feed come out in their original order. Since every feed is non-decreasing, the heap minimum is always the smallest remaining (timestamp, feed index, position) key overall, which is exactly the required processing order.

Edge cases: empty feeds are never pushed, and an empty input (or only empty feeds) leaves the heap empty so the answer is []. The running price is accumulated in a 64-bit integer because it can reach 10^11 in magnitude, and it is never clamped at zero.

Alternative: collecting all N events with their (timestamp, feed index, position) keys and sorting them also works in O(N log N); the heap version improves this to O(N log K) and only keeps K candidates in memory at once.

Time complexity:
O(N log K + K)
Space complexity:
O(N + K)