Bank System
Problem
Problem Overview
Design and implement a banking system that supports account creation, fund transfers, spending analytics, scheduled payments, and account merging. This is a multi-level problem where each level builds upon the previous one.
All operations receive a timestamp parameter (a positive integer in milliseconds). Timestamps are guaranteed to be non-decreasing across calls (multiple operations can share the same timestamp). Scheduled payments introduce additional execution at their due times.
You'll build this incrementally across four levels.
Level 1: Basic Banking Operations
Problem Statement
Implement a BankSystem class that supports creating accounts, depositing funds, and transferring money between accounts.
class BankSystem:
def __init__(self):
"""Initialize the banking system."""
pass
def create_account(self, timestamp: int, account_id: str) -> bool:
"""
Create a new bank account with a zero balance.
Args:
timestamp: The current time in milliseconds.
account_id: Unique identifier for the account.
Returns:
True if the account was successfully created,
False if an account with the same ID already exists.
"""
pass
def deposit(self, timestamp: int, account_id: str, amount: int) -> bool:
"""
Deposit funds into an existing account.
Args:
timestamp: The current time in milliseconds.
account_id: The account to deposit into.
amount: The amount to deposit (positive integer).
Returns:
True if the deposit was successful,
False if the account does not exist.
"""
pass
def transfer(self, timestamp: int, source_id: str, target_id: str, amount: int) -> bool:
"""
Transfer funds from one account to another.
Args:
timestamp: The current time in milliseconds.
source_id: The account to transfer from.
target_id: The account to transfer to.
amount: The amount to transfer (positive integer).
Returns:
True if the transfer was successful,
False if either account does not exist, source and target
are the same, or source has insufficient funds.
"""
pass
Example Usage
bank = BankSystem()
bank.create_account(1, "acc1") # True
bank.create_account(2, "acc2") # True
bank.create_account(3, "acc1") # False (duplicate)
bank.deposit(4, "acc1", 1000) # True
bank.deposit(5, "acc3", 500) # False (account doesn't exist)
bank.transfer(6, "acc1", "acc2", 300) # True (acc1: 700, acc2: 300)
bank.transfer(7, "acc1", "acc2", 800) # False (insufficient funds)
bank.transfer(8, "acc1", "acc1", 100) # False (same account)
Level 2: Top Spenders
Problem Statement
Extend the system to track outgoing transfer amounts and return the top spenders.
def top_spenders(self, timestamp: int, n: int) -> list:
"""
Return the top N accounts ranked by total outgoing transfer amount.
Args:
timestamp: The current time in milliseconds.
n: The number of top spenders to return.
Returns:
A list of strings in the format "account_id(total_outgoing)",
sorted by total outgoing amount in descending order. If two
accounts have the same total outgoing amount, sort them
alphabetically by account_id. If there are fewer than n
accounts with outgoing transfers, return all of them.
Notes:
- Only successful transfers count toward outgoing totals.
- Deposits do not count as outgoing.
"""
pass
Example Usage
bank = BankSystem()
bank.create_account(1, "acc1")
bank.create_account(2, "acc2")
bank.create_account(3, "acc3")
bank.deposit(4, "acc1", 2000)
bank.deposit(5, "acc2", 1000)
bank.deposit(6, "acc3", 500)
bank.transfer(7, "acc1", "acc2", 500)
bank.transfer(8, "acc2", "acc3", 300)
bank.transfer(9, "acc1", "acc3", 200)
bank.top_spenders(10, 2)
# ["acc1(700)", "acc2(300)"]
# acc1 transferred out 500 + 200 = 700
# acc2 transferred out 300
bank.top_spenders(11, 5)
# ["acc1(700)", "acc2(300)"]
# acc3 has no outgoing transfers, so only 2 results
Level 3: Scheduled Payments
Problem Statement
The system should allow scheduling payments and cancelling them. A scheduled payment is an outgoing payment that will be deducted from the account at a future time (timestamp + delay).
def schedule_payment(self, timestamp: int, account_id: str, amount: int, delay: int) -> str:
"""
Schedule a payment to be executed at timestamp + delay.
Args:
timestamp: The current time in milliseconds.
account_id: The source account for the payment.
amount: The amount to deduct from the account.
delay: The delay in milliseconds before the payment is executed.
Returns:
A unique payment ID in the format "payment[N]" where N is the
ordinal number of the scheduled payment across all accounts
(e.g., "payment1", "payment2", etc.).
Returns "" (empty string) if the account does not exist.
Notes:
- The payment is skipped if the account has insufficient funds
when the payment is due.
- Successful payments should count as outgoing transactions
and be reflected in top_spenders.
- Scheduled payments should be processed BEFORE any other
operations at the given timestamp.
- If multiple scheduled payments are due at the same time,
they should be processed in order of creation (e.g.,
"payment1" before "payment2").
"""
pass
def cancel_payment(self, timestamp: int, account_id: str, payment_id: str) -> bool:
"""
Cancel a scheduled payment.
Args:
timestamp: The current time in milliseconds.
account_id: The account that scheduled the payment.
payment_id: The ID of the payment to cancel.
Returns:
True if the payment was successfully cancelled,
False if the payment_id does not exist, was already
cancelled, was already executed, or if account_id
does not match the source account of the scheduled payment.
Notes:
- Scheduled payments due at the current timestamp must be
processed BEFORE any cancel_payment operations at the
same timestamp.
"""
pass
Processing Order
At any given timestamp, operations are processed in this order:
- Execute all due scheduled payments (in order of creation)
- Process the current operation (deposit, transfer, cancel_payment, etc.)
This means a cancel_payment call cannot cancel a payment that is due at the same timestamp — the payment will have already been executed.
Example Usage
bank = BankSystem()
bank.create_account(1, "acc1")
bank.create_account(2, "acc2")
bank.deposit(3, "acc1", 1000)
# Schedule a payment from acc1 for 500, due at timestamp 103 (3 + 100)
pid1 = bank.schedule_payment(3, "acc1", 500, 100)
# Returns "payment1"
# Schedule another payment from acc1 for 300, due at timestamp 153 (3 + 150)
pid2 = bank.schedule_payment(3, "acc1", 300, 150)
# Returns "payment2"
# Cancel the second payment
bank.cancel_payment(50, "acc1", pid2)
# Returns True
# At timestamp 103, payment1 executes automatically before any operation
# acc1 balance: 1000 - 500 = 500
bank.deposit(103, "acc1", 200)
# acc1 balance: 500 + 200 = 700
# payment2 was cancelled, so nothing happens at timestamp 153
bank.deposit(153, "acc1", 100)
# acc1 balance: 700 + 100 = 800
bank.top_spenders(200, 5)
# ["acc1(500)"] — the scheduled payment counted as outgoing
Level 4: Account Merging and Historical Balance
Problem Statement
Add the ability to merge two accounts and query the balance of an account at a specific point in time.
def merge_accounts(self, timestamp: int, account_id1: str, account_id2: str) -> bool:
"""
Merge account_id2 into account_id1.
Args:
timestamp: The current time in milliseconds.
account_id1: The target account (survives the merge).
account_id2: The source account (removed after merge).
Returns:
True if the merge was successful, False if either account
does not exist or they are the same account.
Notes:
- account_id2's balance is added to account_id1.
- account_id2 is removed from the system after merging.
- Any scheduled payments from account_id2 should be
transferred to account_id1.
- account_id2's outgoing totals should be merged into
account_id1's outgoing totals for top_spenders.
"""
pass
def get_balance(self, timestamp: int, account_id: str, time_at: int) -> int:
"""
Get the balance of an account at a specific historical timestamp.
Args:
timestamp: The current time in milliseconds.
account_id: The account to query.
time_at: The historical timestamp to query the balance at.
Returns:
The balance of the account at time_at, or -1 if the account
did not exist at time_at.
Notes:
- If the account was merged into another account before
time_at, return -1 (the old account no longer existed).
- If querying a timestamp before the account was created,
return -1.
- If the account existed at time_at but has since been
merged away, still return its balance at that time.
Historical queries should work even for accounts that
no longer exist.
"""
pass
Corner Cases
- Merged account queries: After merging
acc2intoacc1, queryingget_balance(ts, "acc2", time_before_merge)should still returnacc2's balance at that time — even thoughacc2no longer exists. - Post-merge queries: Querying
get_balance(ts, "acc2", time_after_merge)should return-1sinceacc2didn't exist after the merge. - Scheduled payment transfer: Scheduled payments from the merged account should be reassigned to the surviving account.
Example Usage
bank = BankSystem()
bank.create_account(1, "acc1")
bank.create_account(2, "acc2")
bank.deposit(3, "acc1", 1000)
bank.deposit(4, "acc2", 500)
bank.transfer(5, "acc1", "acc2", 200)
# acc1: 800, acc2: 700
bank.merge_accounts(6, "acc1", "acc2")
# acc1: 800 + 700 = 1500, acc2: removed
# Historical query for acc2 before merge — should still work
bank.get_balance(7, "acc2", 5)
# 700 (acc2's balance at timestamp 5)
# Historical query for acc2 after merge — account didn't exist
bank.get_balance(8, "acc2", 6)
# -1
# Current balance of acc1
bank.get_balance(9, "acc1", 9)
# 1500
Follow-Up Discussion Topics
Solution
Sign in to get AI feedback on your answer. Your work is saved while you do.
Sign in to evaluate