2043. Simple Bank System
Problem Description
This problem asks you to implement a simple banking system that can handle three types of transactions: transfers, deposits, and withdrawals.
You need to create a Bank
class with the following features:
Initial Setup:
- The bank has
n
accounts numbered from1
ton
(note: 1-indexed) - You're given an initial
balance
array wherebalance[i]
represents the initial balance of account(i + 1)
Operations to Implement:
-
Constructor
Bank(balance)
: Initialize the bank with the given balance array -
transfer(account1, account2, money)
: Transfermoney
dollars fromaccount1
toaccount2
- Returns
true
if successful,false
otherwise - The transaction is valid only if:
- Both account numbers are between
1
andn
account1
has sufficient balance (≥money
)
- Both account numbers are between
- Returns
-
deposit(account, money)
: Addmoney
dollars to the specified account- Returns
true
if successful,false
otherwise - The transaction is valid only if the account number is between
1
andn
- Returns
-
withdraw(account, money)
: Removemoney
dollars from the specified account- Returns
true
if successful,false
otherwise - The transaction is valid only if:
- The account number is between
1
andn
- The account has sufficient balance (≥
money
)
- The account number is between
- Returns
Key Points:
- Account numbers are 1-indexed (accounts are numbered
1
ton
) - The balance array is 0-indexed (standard array indexing)
- All invalid transactions should return
false
without modifying any balances - Valid transactions should update the balances and return
true
Intuition
The key insight is that this is a straightforward simulation problem - we need to maintain the state of all bank accounts and update them based on valid transactions.
Since we need to track the balance of multiple accounts and access them by account number, an array is the natural choice for storing balances. The main challenge is handling the index conversion correctly: accounts are numbered from 1
to n
, but arrays are 0-indexed. So account k
corresponds to balance[k-1]
.
For each operation, we follow a simple pattern:
- First validate - check if the transaction can proceed
- Then execute - only modify balances if validation passes
The validation logic follows directly from the requirements:
- For any operation, the account number must be valid (between
1
andn
) - For operations that remove money (withdraw and transfer), we must ensure sufficient balance exists
Why check account validity as account > n
instead of account < 1 || account > n
? Since account numbers are positive integers in this context, we only need to check the upper bound. Invalid negative or zero account numbers would naturally fail this check.
The beauty of this solution is its simplicity - we don't need complex data structures or algorithms. Just an array to store balances and careful validation before each operation. Each method performs constant time operations: array access, comparison, and basic arithmetic, making the solution efficient.
The separation of validation and execution also makes the code clean and maintainable - if a transaction fails validation, we return false
immediately without touching any balances, ensuring data consistency.
Solution Approach
The implementation uses a simple simulation approach with an array to store account balances.
Data Structure:
- Use an array
balance
to store the balance of each account - Store the total number of accounts
n
as a member variable for quick validation
Implementation Details:
-
Constructor (
__init__
):- Store the input
balance
array directly as a member variable - Calculate and store
n = len(balance)
for boundary checking - This gives us O(1) access to any account's balance
- Store the input
-
Transfer Function:
- Validation: Check if
account1 > n
oraccount2 > n
(invalid account numbers) - Check if
balance[account1 - 1] < money
(insufficient funds) - If any validation fails, return
false
- Otherwise, execute the transfer:
balance[account1 - 1] -= money
(deduct from source)balance[account2 - 1] += money
(add to destination)
- Return
true
after successful transfer
- Validation: Check if
-
Deposit Function:
- Validation: Check if
account > n
(invalid account number) - If validation fails, return
false
- Otherwise, add money:
balance[account - 1] += money
- Return
true
after successful deposit
- Validation: Check if
-
Withdraw Function:
- Validation: Check if
account > n
(invalid account number) - Check if
balance[account - 1] < money
(insufficient funds) - If any validation fails, return
false
- Otherwise, deduct money:
balance[account - 1] -= money
- Return
true
after successful withdrawal
- Validation: Check if
Index Conversion:
The critical detail is converting between 1-indexed account numbers and 0-indexed array positions. For account number k
, we access balance[k - 1]
in the array.
Time Complexity:
- All operations (
transfer
,deposit
,withdraw
) run in O(1) time - Each operation performs a fixed number of array accesses and comparisons
Space Complexity:
- O(n) space to store the balance array where n is the number of accounts
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate how the banking system works.
Initial Setup:
balance = [100, 50, 200]
This creates a bank with 3 accounts:
- Account 1: $100 (stored at index 0)
- Account 2: $50 (stored at index 1)
- Account 3: $200 (stored at index 2)
Operation 1: Transfer $30 from Account 1 to Account 3
transfer(1, 3, 30)
- Validation:
- Is account 1 valid? 1 ≤ 3 ✓
- Is account 3 valid? 3 ≤ 3 ✓
- Does account 1 have ≥ $30? balance[0] = 100 ≥ 30 ✓
- Execution:
- balance[0] = 100 - 30 = 70
- balance[2] = 200 + 30 = 230
- Result: Returns
true
- New balances: [70, 50, 230]
Operation 2: Withdraw $80 from Account 2
withdraw(2, 80)
- Validation:
- Is account 2 valid? 2 ≤ 3 ✓
- Does account 2 have ≥ $80? balance[1] = 50 < 80 ✗
- Result: Returns
false
(insufficient funds) - Balances unchanged: [70, 50, 230]
Operation 3: Deposit $25 to Account 2
deposit(2, 25)
- Validation:
- Is account 2 valid? 2 ≤ 3 ✓
- Execution:
- balance[1] = 50 + 25 = 75
- Result: Returns
true
- New balances: [70, 75, 230]
Operation 4: Transfer $100 from Account 4 to Account 1
transfer(4, 1, 100)
- Validation:
- Is account 4 valid? 4 > 3 ✗ (invalid account)
- Result: Returns
false
(invalid account) - Balances unchanged: [70, 75, 230]
This example demonstrates the key aspects:
- Index conversion: Account 1 maps to index 0, Account 2 to index 1, etc.
- Validation before execution: Each operation checks validity first
- Balance preservation: Failed operations don't modify any balances
- Successful updates: Valid operations update the appropriate balances and return true
Solution Implementation
1from typing import List
2
3class Bank:
4 def __init__(self, balance: List[int]):
5 """
6 Initialize the bank with initial account balances.
7
8 Args:
9 balance: List of initial balances where balance[i] is the balance of account i+1
10 """
11 self.balance = balance
12 self.n = len(balance)
13
14 def transfer(self, account1: int, account2: int, money: int) -> bool:
15 """
16 Transfer money from account1 to account2.
17
18 Args:
19 account1: Source account number (1-indexed)
20 account2: Destination account number (1-indexed)
21 money: Amount to transfer
22
23 Returns:
24 True if transfer successful, False otherwise
25 """
26 # Check if accounts are valid and source account has sufficient funds
27 if account1 > self.n or account2 > self.n or self.balance[account1 - 1] < money:
28 return False
29
30 # Perform the transfer
31 self.balance[account1 - 1] -= money
32 self.balance[account2 - 1] += money
33 return True
34
35 def deposit(self, account: int, money: int) -> bool:
36 """
37 Deposit money into an account.
38
39 Args:
40 account: Account number (1-indexed)
41 money: Amount to deposit
42
43 Returns:
44 True if deposit successful, False otherwise
45 """
46 # Check if account is valid
47 if account > self.n:
48 return False
49
50 # Add money to the account
51 self.balance[account - 1] += money
52 return True
53
54 def withdraw(self, account: int, money: int) -> bool:
55 """
56 Withdraw money from an account.
57
58 Args:
59 account: Account number (1-indexed)
60 money: Amount to withdraw
61
62 Returns:
63 True if withdrawal successful, False otherwise
64 """
65 # Check if account is valid and has sufficient funds
66 if account > self.n or self.balance[account - 1] < money:
67 return False
68
69 # Withdraw money from the account
70 self.balance[account - 1] -= money
71 return True
72
73
74# Your Bank object will be instantiated and called as such:
75# obj = Bank(balance)
76# param_1 = obj.transfer(account1, account2, money)
77# param_2 = obj.deposit(account, money)
78# param_3 = obj.withdraw(account, money)
79
1/**
2 * Bank class that simulates basic banking operations
3 * Accounts are 1-indexed (account numbers start from 1)
4 */
5class Bank {
6 // Array to store account balances
7 private long[] accountBalances;
8 // Total number of accounts in the bank
9 private int numberOfAccounts;
10
11 /**
12 * Constructor to initialize the bank with initial account balances
13 * @param balance Array of initial balances for each account
14 */
15 public Bank(long[] balance) {
16 this.accountBalances = balance;
17 this.numberOfAccounts = balance.length;
18 }
19
20 /**
21 * Transfer money from one account to another
22 * @param account1 Source account number (1-indexed)
23 * @param account2 Destination account number (1-indexed)
24 * @param money Amount of money to transfer
25 * @return true if transfer is successful, false otherwise
26 */
27 public boolean transfer(int account1, int account2, long money) {
28 // Validate both account numbers exist and source account has sufficient balance
29 if (account1 > numberOfAccounts || account2 > numberOfAccounts ||
30 accountBalances[account1 - 1] < money) {
31 return false;
32 }
33
34 // Deduct money from source account
35 accountBalances[account1 - 1] -= money;
36 // Add money to destination account
37 accountBalances[account2 - 1] += money;
38
39 return true;
40 }
41
42 /**
43 * Deposit money into an account
44 * @param account Account number (1-indexed)
45 * @param money Amount of money to deposit
46 * @return true if deposit is successful, false otherwise
47 */
48 public boolean deposit(int account, long money) {
49 // Validate account number exists
50 if (account > numberOfAccounts) {
51 return false;
52 }
53
54 // Add money to the account
55 accountBalances[account - 1] += money;
56
57 return true;
58 }
59
60 /**
61 * Withdraw money from an account
62 * @param account Account number (1-indexed)
63 * @param money Amount of money to withdraw
64 * @return true if withdrawal is successful, false otherwise
65 */
66 public boolean withdraw(int account, long money) {
67 // Validate account number exists and has sufficient balance
68 if (account > numberOfAccounts || accountBalances[account - 1] < money) {
69 return false;
70 }
71
72 // Deduct money from the account
73 accountBalances[account - 1] -= money;
74
75 return true;
76 }
77}
78
79/**
80 * Your Bank object will be instantiated and called as such:
81 * Bank obj = new Bank(balance);
82 * boolean param_1 = obj.transfer(account1,account2,money);
83 * boolean param_2 = obj.deposit(account,money);
84 * boolean param_3 = obj.withdraw(account,money);
85 */
86
1class Bank {
2private:
3 vector<long long> accountBalances; // Store balance for each account
4 int totalAccounts; // Total number of accounts in the bank
5
6public:
7 /**
8 * Constructor: Initialize bank with given account balances
9 * @param balance: Initial balance for each account (1-indexed in usage)
10 */
11 Bank(vector<long long>& balance) {
12 accountBalances = balance;
13 totalAccounts = balance.size();
14 }
15
16 /**
17 * Transfer money from one account to another
18 * @param account1: Source account number (1-indexed)
19 * @param account2: Destination account number (1-indexed)
20 * @param money: Amount to transfer
21 * @return: true if transfer successful, false otherwise
22 */
23 bool transfer(int account1, int account2, long long money) {
24 // Validate both accounts exist and source account has sufficient funds
25 if (account1 < 1 || account1 > totalAccounts ||
26 account2 < 1 || account2 > totalAccounts ||
27 accountBalances[account1 - 1] < money) {
28 return false;
29 }
30
31 // Perform the transfer
32 accountBalances[account1 - 1] -= money;
33 accountBalances[account2 - 1] += money;
34 return true;
35 }
36
37 /**
38 * Deposit money into an account
39 * @param account: Account number (1-indexed)
40 * @param money: Amount to deposit
41 * @return: true if deposit successful, false otherwise
42 */
43 bool deposit(int account, long long money) {
44 // Validate account exists
45 if (account < 1 || account > totalAccounts) {
46 return false;
47 }
48
49 // Add money to the account
50 accountBalances[account - 1] += money;
51 return true;
52 }
53
54 /**
55 * Withdraw money from an account
56 * @param account: Account number (1-indexed)
57 * @param money: Amount to withdraw
58 * @return: true if withdrawal successful, false otherwise
59 */
60 bool withdraw(int account, long long money) {
61 // Validate account exists and has sufficient funds
62 if (account < 1 || account > totalAccounts ||
63 accountBalances[account - 1] < money) {
64 return false;
65 }
66
67 // Deduct money from the account
68 accountBalances[account - 1] -= money;
69 return true;
70 }
71};
72
73/**
74 * Your Bank object will be instantiated and called as such:
75 * Bank* obj = new Bank(balance);
76 * bool param_1 = obj->transfer(account1, account2, money);
77 * bool param_2 = obj->deposit(account, money);
78 * bool param_3 = obj->withdraw(account, money);
79 */
80
1// Array to store account balances
2let accountBalances: number[] = [];
3
4/**
5 * Initializes the bank with given account balances
6 * @param balance - Array of initial balances for each account
7 */
8function initializeBank(balance: number[]): void {
9 accountBalances = balance;
10}
11
12/**
13 * Transfers money from one account to another
14 * @param account1 - Source account number (1-indexed)
15 * @param account2 - Destination account number (1-indexed)
16 * @param money - Amount to transfer
17 * @returns true if transfer successful, false otherwise
18 */
19function transfer(account1: number, account2: number, money: number): boolean {
20 // Validate account numbers and check sufficient balance
21 if (
22 account1 > accountBalances.length ||
23 account2 > accountBalances.length ||
24 money > accountBalances[account1 - 1]
25 ) {
26 return false;
27 }
28
29 // Perform the transfer
30 accountBalances[account1 - 1] -= money;
31 accountBalances[account2 - 1] += money;
32
33 return true;
34}
35
36/**
37 * Deposits money into an account
38 * @param account - Account number (1-indexed)
39 * @param money - Amount to deposit
40 * @returns true if deposit successful, false otherwise
41 */
42function deposit(account: number, money: number): boolean {
43 // Validate account number
44 if (account > accountBalances.length) {
45 return false;
46 }
47
48 // Add money to the account
49 accountBalances[account - 1] += money;
50
51 return true;
52}
53
54/**
55 * Withdraws money from an account
56 * @param account - Account number (1-indexed)
57 * @param money - Amount to withdraw
58 * @returns true if withdrawal successful, false otherwise
59 */
60function withdraw(account: number, money: number): boolean {
61 // Validate account number and check sufficient balance
62 if (
63 account > accountBalances.length ||
64 money > accountBalances[account - 1]
65 ) {
66 return false;
67 }
68
69 // Deduct money from the account
70 accountBalances[account - 1] -= money;
71
72 return true;
73}
74
Time and Space Complexity
Time Complexity: Each operation (transfer
, deposit
, withdraw
) has a time complexity of O(1)
. All operations perform a constant number of operations regardless of the input size - they only involve simple comparisons, arithmetic operations, and direct array access using indices, which all take constant time.
Space Complexity: The overall space complexity is O(n)
, where n
is the length of the balance
list. The class stores the balance
list which contains n
elements representing account balances. The additional variables self.n
and temporary variables within methods use O(1)
space, so the dominant factor is the storage of the balance
list itself.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Check Lower Bound for Account Numbers
The current implementation only checks if account numbers exceed n
but doesn't verify they're at least 1. Since accounts are 1-indexed, account number 0 or negative numbers would cause incorrect array access.
Problem Example:
bank = Bank([100, 200]) bank.withdraw(0, 50) # Would access balance[-1], which is valid but wrong! bank.transfer(-1, 1, 50) # Would access balance[-2], also valid but wrong!
Solution: Add lower bound validation to all methods:
def transfer(self, account1: int, account2: int, money: int) -> bool:
# Check both upper AND lower bounds
if (account1 < 1 or account1 > self.n or
account2 < 1 or account2 > self.n or
self.balance[account1 - 1] < money):
return False
# ... rest of the code
def deposit(self, account: int, money: int) -> bool:
if account < 1 or account > self.n:
return False
# ... rest of the code
def withdraw(self, account: int, money: int) -> bool:
if account < 1 or account > self.n or self.balance[account - 1] < money:
return False
# ... rest of the code
2. Not Validating Negative Money Amounts
The implementation doesn't check if the money amount is negative. While some systems might want to allow this (negative deposit = withdrawal), it's typically a bug and can lead to unexpected behavior.
Problem Example:
bank = Bank([100, 200]) bank.deposit(1, -50) # Would actually subtract money! bank.withdraw(1, -50) # Would actually add money! bank.transfer(1, 2, -50) # Would transfer money backwards!
Solution: Add validation for non-negative amounts:
def transfer(self, account1: int, account2: int, money: int) -> bool:
if money < 0:
return False
# ... rest of validation
def deposit(self, account: int, money: int) -> bool:
if money < 0:
return False
# ... rest of validation
def withdraw(self, account: int, money: int) -> bool:
if money < 0:
return False
# ... rest of validation
3. Integer Overflow Concerns
In languages with fixed-size integers or when dealing with very large amounts, adding money to an account could cause integer overflow.
Problem Example:
# If using a language with fixed integer size bank = Bank([2**31 - 100]) # Near max int bank.deposit(1, 200) # Could overflow in some languages
Solution: Check for potential overflow before performing operations:
def deposit(self, account: int, money: int) -> bool:
if account < 1 or account > self.n:
return False
# Check for potential overflow (Python handles this automatically,
# but other languages might need explicit checks)
if self.balance[account - 1] > sys.maxsize - money:
return False # Would overflow
self.balance[account - 1] += money
return True
4. Modifying the Original Balance Array
The constructor stores a reference to the input array rather than creating a copy. If the caller modifies the original array after creating the Bank object, it could corrupt the bank's state.
Problem Example:
initial_balance = [100, 200] bank = Bank(initial_balance) initial_balance[0] = 1000 # This modifies the bank's internal state!
Solution: Create a defensive copy of the input array:
def __init__(self, balance: List[int]):
self.balance = balance.copy() # Create a copy instead of using reference
self.n = len(self.balance)
In a binary min heap, the maximum element can be found in:
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!