Implement a Timestamped Banking System
Company: Meta
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Take-home Project
Quick Answer: This question evaluates the ability to design and implement an in-memory, time-ordered stateful system handling event scheduling, transactional updates, historical queries, merging state, and ranked aggregations using appropriate data structures and algorithms, and it belongs to the Coding & Algorithms domain.
Part 1: Basic Timestamped Account Operations
Constraints
- 0 <= len(queries) <= 20000
- 1 <= amount <= 10^9
- account_id is a non-empty string
- Timestamps are integers and queries should be processed in the given order
Examples
Input: ([['CREATE', 1, 'alice'], ['DEPOSIT', 2, 'alice', 100], ['CREATE', 3, 'bob'], ['TRANSFER', 4, 'alice', 'bob', 30], ['DEPOSIT', 5, 'bob', 10]],)
Expected Output: [True, 100, True, 70, 40]
Explanation: Alice is created and funded. After transferring 30 to Bob, Alice has 70. Bob then deposits 10 and reaches 40.
Input: ([['CREATE', 1, 'a'], ['CREATE', 2, 'a'], ['DEPOSIT', 3, 'missing', 50], ['TRANSFER', 4, 'a', 'missing', 10]],)
Expected Output: [True, False, None, None]
Explanation: Duplicate creation fails, depositing into a missing account fails, and transferring to a missing account also fails.
Input: ([['CREATE', 1, 'x'], ['CREATE', 2, 'y'], ['DEPOSIT', 3, 'x', 20], ['TRANSFER', 4, 'x', 'x', 5], ['TRANSFER', 5, 'x', 'y', 25], ['TRANSFER', 6, 'x', 'y', 20]],)
Expected Output: [True, True, 20, None, None, 0]
Explanation: A self-transfer is invalid, an overdraft transfer is invalid, and the final valid transfer leaves x with balance 0.
Input: ([],)
Expected Output: []
Explanation: Edge case: no queries means no outputs.
Hints
- A hash map from account_id to current balance is enough for this level.
- For TRANSFER, check every failure condition before changing either balance.
Part 2: Rank the Top Spenders
Constraints
- 0 <= len(queries) <= 20000
- 1 <= amount <= 10^9
- 1 <= n <= 20000
- account_id is a non-empty string
- Only successful TRANSFER operations contribute to outgoing totals
Examples
Input: [('CREATE', 'alice'), ('CREATE', 'bob'), ('DEPOSIT', 'alice', 100), ('DEPOSIT', 'bob', 20), ('TRANSFER', 'alice', 'bob', 30), ('TRANSFER', 'bob', 'alice', 20)]
Expected Output: [True, True, 100, 20, 70, 30]
Explanation: After the first transfer, alice has 70. After the second transfer, bob has 30, and TRANSFER returns the source account's balance.
Input: [('DEPOSIT', 'ghost', 10), ('CREATE', 'a'), ('CREATE', 'a'), ('TRANSFER', 'a', 'a', 5), ('DEPOSIT', 'a', 0)]
Expected Output: [None, True, False, None, None]
Explanation: Depositing to a missing account fails, duplicate creation returns False, self-transfer is invalid, and non-positive deposits are invalid.
Input: [('CREATE', 'a'), ('CREATE', 'b'), ('DEPOSIT', 'a', 20), ('TRANSFER', 'a', 'b', 25), ('TRANSFER', 'a', 'b', 20)]
Expected Output: [True, True, 20, None, 0]
Explanation: The first transfer fails because account a only has 20. The second succeeds and leaves a with 0.
Input: []
Expected Output: []
Explanation: No queries means no results.
Hints
- Store two values per account: current balance and total outgoing amount.
- For TOP, sort accounts using the key `(-outgoing, account_id)`.
Part 3: Scheduled Payments in a Banking System
Constraints
- 0 <= len(queries) <= 20000
- Queries are given in nondecreasing timestamp order
- 1 <= amount <= 10^9
- 1 <= delay <= 10^9
- account_id is a non-empty string
Examples
Input: ([['CREATE', 1, 'a'], ['DEPOSIT', 2, 'a', 100], ['SCHEDULE', 3, 'a', 30, 5], ['BALANCE', 7, 'a'], ['BALANCE', 8, 'a'], ['CANCEL', 9, 'a', 'payment1']],)
Expected Output: [True, 100, 'payment1', 100, 70, False]
Explanation: The payment is scheduled for time 8. The balance is still 100 at time 7, becomes 70 at time 8, and can no longer be canceled afterward.
Input: ([['CREATE', 1, 'a'], ['DEPOSIT', 2, 'a', 50], ['SCHEDULE', 3, 'a', 40, 4], ['SCHEDULE', 4, 'a', 20, 3], ['BALANCE', 7, 'a']],)
Expected Output: [True, 50, 'payment1', 'payment2', 10]
Explanation: Both payments are due at time 7. `payment1` executes first and leaves balance 10, so `payment2` fails due to insufficient funds.
Input: ([['CREATE', 1, 'x'], ['DEPOSIT', 2, 'x', 25], ['SCHEDULE', 3, 'x', 10, 2], ['CANCEL', 4, 'x', 'payment1'], ['BALANCE', 5, 'x'], ['CANCEL', 6, 'x', 'payment1']],)
Expected Output: [True, 25, 'payment1', True, 25, False]
Explanation: The payment is canceled before it is due, so the balance remains unchanged. Canceling the same payment again fails.
Input: ([],)
Expected Output: []
Explanation: Edge case: no queries.
Hints
- A min-heap ordered by `(execution_time, creation_order)` lets you run due payments efficiently.
- Be careful with query order: due payments at time t happen before processing a CANCEL or BALANCE query at time t.
Part 4: Merge Accounts and Query Historical Balances
Constraints
- 0 <= len(queries) <= 20000
- Queries are given in strictly increasing timestamp order
- Each account_id is created at most once in the entire input
- 1 <= amount <= 10^9
- For every GET_BALANCE query, `time_at` is an integer with `time_at <= timestamp`
Examples
Input: ([['CREATE', 1, 'a'], ['CREATE', 2, 'b'], ['DEPOSIT', 3, 'a', 50], ['DEPOSIT', 4, 'b', 30], ['MERGE', 5, 'a', 'b'], ['GET_BALANCE', 6, 'a', 4], ['GET_BALANCE', 7, 'a', 5], ['GET_BALANCE', 8, 'b', 4], ['GET_BALANCE', 9, 'b', 5]],)
Expected Output: [True, True, 50, 30, True, 50, 80, 30, None]
Explanation: Before the merge, a had 50 and b had 30. At time 5, a becomes 80 and b stops existing.
Input: ([['CREATE', 1, 'x'], ['CREATE', 2, 'y'], ['DEPOSIT', 3, 'x', 100], ['TRANSFER', 4, 'x', 'y', 40], ['GET_BALANCE', 5, 'x', 3], ['GET_BALANCE', 6, 'y', 4], ['MERGE', 7, 'y', 'x'], ['GET_BALANCE', 8, 'y', 7], ['GET_BALANCE', 9, 'x', 6], ['GET_BALANCE', 10, 'x', 7]],)
Expected Output: [True, True, 100, 60, 100, 40, True, 100, 60, None]
Explanation: After the transfer, x has 60 and y has 40. Merging x into y at time 7 makes y equal 100 and removes x from that time on.
Input: ([['CREATE', 1, 'a'], ['MERGE', 2, 'a', 'a'], ['GET_BALANCE', 3, 'a', 0], ['GET_BALANCE', 4, 'missing', 1], ['DEPOSIT', 5, 'missing', 10]],)
Expected Output: [True, False, None, None, None]
Explanation: Merging an account with itself is invalid. Historical queries before creation or for unknown accounts return None.
Input: ([],)
Expected Output: []
Explanation: Edge case: no queries.
Hints
- Store only balance changes: for each account, keep a list of `(timestamp, balance)` entries.
- To answer GET_BALANCE fast, binary search for the last history entry whose timestamp is `<= time_at`, and also check creation and deletion times.