Simple Bank System solution and explanation
9 more videos on this problem
Simple Bank System | Easy Simulation| Leetcode 2043 | codestorywithMIKcodestorywithMIK · 11:08 · 5K views
Leetcode Solution - 2043 Simple Bank SystemJohn Leonardo · 4:58 · 1K views
2043. Simple Bank System (Leetcode Medium)Programming Live with Larry · 10:17 · 1K views
2043. Simple Bank System | Leetcode Daily - PythonLeetcode Daily · 9:21 · 760 views
Daily Leetcode #2035: Oct 26, 2025 - 2043. Simple Bank SystemProgramming Live with Larry · 10:42 · 591 views
Simple Bank System | LeetCode 2043 | Java Code | Developer CoderDeveloper Coder · 14:22 · 448 views
Simple Bank System | Leetcode 2043 | Design ProblemSanyam IIT Guwahati · 10:22 · 442 views
Simple Bank System | Leetcode 2043 | DSA | Code With Deepak | Hindi Explanation | Data StructureCode with Deepak · 5:15 · 395 views
Simple Bank System | LeetCode 2043 - Pythonimpoldev · 5:58 · 262 views
2043 Simple Bank System Leetcode CPP SolutionAnimated Anand · 6:51 · 233 views
The problem
You have been tasked with writing a program for a popular bank that will automate all its incoming transactions (transfer, deposit, and withdraw). The bank has n accounts numbered from 1 to n. The initial balance of each account is stored in a 0-indexed integer array balance, with the (i + 1)th account having an initial balance of balance[i].
Execute all the valid transactions. A transaction is valid if:
- The given account number(s) are between
1andn, and - The amount of money withdrawn or transferred from is less than or equal to the balance of the account.
Implement the Bank class:
Bank(long[] balance)Initializes the object with the 0-indexed integer arraybalance.boolean transfer(int account1, int account2, long money)Transfersmoneydollars from the account numberedaccount1to the account numberedaccount2. Returntrueif the transaction was successful,falseotherwise.boolean deposit(int account, long money)Depositmoneydollars into the account numberedaccount. Returntrueif the transaction was successful,falseotherwise.boolean withdraw(int account, long money)Withdrawmoneydollars from the account numberedaccount. Returntrueif the transaction was successful,falseotherwise.
Example 1:
Input
["Bank", "withdraw", "transfer", "deposit", "transfer", "withdraw"]
[[[10, 100, 20, 50, 30]], [3, 10], [5, 1, 20], [5, 20], [3, 4, 15], [10, 50]]
Output
[null, true, true, true, false, false]
Explanation
Bank bank = new Bank([10, 100, 20, 50, 30]);
bank.withdraw(3, 10); // return true, account 3 has a balance of $20, so it is valid to withdraw $10.
// Account 3 has $20 - $10 = $10.
bank.transfer(5, 1, 20); // return true, account 5 has a balance of $30, so it is valid to transfer $20.
// Account 5 has $30 - $20 = $10, and account 1 has $10 + $20 = $30.
bank.deposit(5, 20); // return true, it is valid to deposit $20 to account 5.
// Account 5 has $10 + $20 = $30.
bank.transfer(3, 4, 15); // return false, the current balance of account 3 is $10,
// so it is invalid to transfer $15 from it.
bank.withdraw(10, 50); // return false, it is invalid because account 10 does not exist.
Constraints:
n == balance.length1 <= n, account, account1, account2 <= 1050 <= balance[i], money <= 1012- At most
104calls will be made to each functiontransfer,deposit,withdraw.
Approach overview
Problem Overview: You need to design a simple banking system that stores balances for n accounts and supports three operations: transfer, deposit, and withdraw. Each operation must validate account numbers and ensure sufficient balance before modifying the stored amounts.
Approach 1: Direct Array Manipulation (O(1) per operation, O(n) space)
The most straightforward design stores account balances in an array where the index represents the account number. Every operation performs constant-time access using direct indexing. For example, a transfer checks if both accounts exist, verifies that the source account has enough funds, then subtracts from one index and adds to the other.
This approach works because arrays provide O(1) random access. Validation simply checks if the account index falls within the valid range. Deposits increment the balance at the target index, while withdrawals subtract after confirming sufficient funds. The implementation stays clean and efficient since each operation touches only a few array positions.
This design aligns well with problems involving fixed-size collections and predictable identifiers. When account IDs map directly to indices, arrays are faster and simpler than extra data structures. Concepts here closely relate to array operations and straightforward simulation of system behavior.
Approach 2: HashMap for Account Balances (O(1) average per operation, O(n) space)
A more flexible design stores balances in a HashMap where the key is the account number and the value is the balance. Each operation performs constant-time average lookups using a hash table. Before modifying balances, the system checks whether the relevant keys exist in the map.
Transfers involve two hash lookups followed by updates to both accounts. Deposits update the value for the target account, while withdrawals check the stored balance before subtracting. HashMap-based storage removes reliance on contiguous indices and works even if account numbers are sparse or dynamically created.
This design demonstrates common system design patterns where entities are mapped to values using a hash table. It becomes useful when identifiers are not guaranteed to align with array indices or when the system might expand dynamically.
Recommended for interviews: The array-based implementation is typically expected because the problem provides account balances in order and account numbers map directly to positions. It shows you recognize the simplest constant-time design. The HashMap approach still demonstrates good understanding of scalable system modeling and hash-based lookups.
Approach 1: Direct Array Manipulation
This approach uses a simple array to store the balance of each account starting from account 1 at index 0. All operations check for validity by ensuring accounts fit within valid range and ensuring sufficient funds for transactions before modifying the array accordingly.
class Bank:
def __init__(self, balance):
self.balance = balance
def transfer(self, account1, account2, money):
if account1 <= 0 or account1 > len(self.balance) or account2 <= 0 or account2 > len(self.balance):
return False
if self.balance[account1 - 1] < money:
return False
self.balance[account1 - 1] -= money
self.balance[account2 - 1] += money
return True
def deposit(self, account, money):
if account <= 0 or account > len(self.balance):
return False
self.balance[account - 1] += money
return True
def withdraw(self, account, money):
if account <= 0 or account > len(self.balance) or self.balance[account - 1] < money:
return False
self.balance[account - 1] -= money
return Trueclass Bank {
private long[] balance;
public Bank(long[] balance) {
this.balance = balance;
}
public boolean transfer(int account1, int account2, long money) {
if (account1 <= 0 || account1 > balance.length || account2 <= 0 || account2 > balance.length) return false;
if (balance[account1 - 1] < money) return false;
balance[account1 - 1] -= money;
balance[account2 - 1] += money;
return true;
}
public boolean deposit(int account, long money) {
if (account <= 0 || account > balance.length) return false;
balance[account - 1] += money;
return true;
}
public boolean withdraw(int account, long money) {
if (account <= 0 || account > balance.length || balance[account - 1] < money) return false;
balance[account - 1] -= money;
return true;
}
}#include <vector>
class Bank {
public:
std::vector<long> balance;
Bank(std::vector<long>& balance) : balance(balance) {}
bool transfer(int account1, int account2, long money) {
if (account1 <= 0 || account1 > balance.size() || account2 <= 0 || account2 > balance.size()) return false;
if (balance[account1 - 1] < money) return false;
balance[account1 - 1] -= money;
balance[account2 - 1] += money;
return true;
}
bool deposit(int account, long money) {
if (account <= 0 || account > balance.size()) return false;
balance[account - 1] += money;
return true;
}
bool withdraw(int account, long money) {
if (account <= 0 || account > balance.size() || balance[account - 1] < money) return false;
balance[account - 1] -= money;
return true;
}
};class Bank {
constructor(balance) {
this.balance = balance;
}
transfer(account1, account2, money) {
if (account1 <= 0 || account1 > this.balance.length || account2 <= 0 || account2 > this.balance.length) return false;
if (this.balance[account1 - 1] < money) return false;
this.balance[account1 - 1] -= money;
this.balance[account2 - 1] += money;
return true;
}
deposit(account, money) {
if (account <= 0 || account > this.balance.length) return false;
this.balance[account - 1] += money;
return true;
}
withdraw(account, money) {
if (account <= 0 || account > this.balance.length || this.balance[account - 1] < money) return false;
this.balance[account - 1] -= money;
return true;
}
}#include <stdbool.h>
typedef struct {
long* balance;
int size;
} Bank;
Bank* bankCreate(long* balance, int size) {
Bank* obj = (Bank*)malloc(sizeof(Bank));
obj->balance = balance;
obj->size = size;
return obj;
}
bool bankTransfer(Bank* obj, int account1, int account2, long money) {
if (account1 <= 0 || account1 > obj->size || account2 <= 0 || account2 > obj->size) return false;
if (obj->balance[account1 - 1] < money) return false;
obj->balance[account1 - 1] -= money;
obj->balance[account2 - 1] += money;
return true;
}
bool bankDeposit(Bank* obj, int account, long money) {
if (account <= 0 || account > obj->size) return false;
obj->balance[account - 1] += money;
return true;
}
bool bankWithdraw(Bank* obj, int account, long money) {
if (account <= 0 || account > obj->size || obj->balance[account - 1] < money) return false;
obj->balance[account - 1] -= money;
return true;
}
void bankFree(Bank* obj) {
free(obj);
}public class Bank {
private long[] balance;
public Bank(long[] balance) {
this.balance = balance;
}
public bool Transfer(int account1, int account2, long money) {
if (account1 <= 0 || account1 > balance.Length || account2 <= 0 || account2 > balance.Length) return false;
if (balance[account1 - 1] < money) return false;
balance[account1 - 1] -= money;
balance[account2 - 1] += money;
return true;
}
public bool Deposit(int account, long money) {
if (account <= 0 || account > balance.Length) return false;
balance[account - 1] += money;
return true;
}
public bool Withdraw(int account, long money) {
if (account <= 0 || account > balance.Length || balance[account - 1] < money) return false;
balance[account - 1] -= money;
return true;
}
}The Python solution offers a simple class structure, where methods perform checks and updates on the balance list with index adjustments for account numbers.
Time Complexity: O(1), constant time operations per method.
Space Complexity: O(1), as there is no additional space usage apart from the initial storage.
Approach 2: HashMap for Account Balances
In this approach, we can use a hashmap (or dictionary for Python, Map for Java, etc.) where keys are account numbers and values are their respective balance amounts. It allows for a more dynamic setup, even though it's less efficient than direct array access due to its average constant time complexity for reads and writes.
class Bank:
def __init__(self, balance):
self.balance = {i + 1: balance[i] for i in range(len(balance))}
def transfer(self, account1, account2, money):
if account1 not in self.balance or account2 not in self.balance:
return False
if self.balance[account1] < money:
return False
self.balance[account1] -= money
self.balance[account2] += money
return True
def deposit(self, account, money):
if account not in self.balance:
return False
self.balance[account] += money
return True
def withdraw(self, account, money):
if account not in self.balance or self.balance[account] < money:
return False
self.balance[account] -= money
return Trueimport java.util.HashMap;
import java.util.Map;
class Bank {
private Map<Integer, Long> balanceMap;
public Bank(long[] balance) {
balanceMap = new HashMap<>();
for (int i = 0; i < balance.length; i++) {
balanceMap.put(i + 1, balance[i]);
}
}
public boolean transfer(int account1, int account2, long money) {
if (!balanceMap.containsKey(account1) || !balanceMap.containsKey(account2)) return false;
if (balanceMap.get(account1) < money) return false;
balanceMap.put(account1, balanceMap.get(account1) - money);
balanceMap.put(account2, balanceMap.get(account2) + money);
return true;
}
public boolean deposit(int account, long money) {
if (!balanceMap.containsKey(account)) return false;
balanceMap.put(account, balanceMap.get(account) + money);
return true;
}
public boolean withdraw(int account, long money) {
if (!balanceMap.containsKey(account) || balanceMap.get(account) < money) return false;
balanceMap.put(account, balanceMap.get(account) - money);
return true;
}
}This Python implementation replaces the balance array with a dictionary to hold account numbers as keys and balance values. Although it slightly increases space usage, it offers flexibility should accounts need to be added dynamically.
Time Complexity: O(1) on average due to hash table efficiency.
Space Complexity: O(n), storing all account balances in a dictionary.
Approach 3: Simulation
We can use an array balance to store the balance of each account. For each operation, we simply perform the required checks and updates according to the problem statement.
For the transfer operation, we need to check whether the account numbers are valid and whether the balance is sufficient. If the conditions are met, we perform the transfer.
For the deposit operation, we only need to check whether the account number is valid, and then perform the deposit.
For the withdraw operation, we need to check whether the account number is valid and whether the balance is sufficient. If the conditions are met, we perform the withdrawal.
Each operation has a time complexity of O(1), so the overall time complexity is O(q), where q is the number of operations. The space complexity is O(1).
class Bank {
balance: number[];
constructor(balance: number[]) {
this.balance = balance;
}
transfer(account1: number, account2: number, money: number): boolean {
if (
account1 > this.balance.length ||
account2 > this.balance.length ||
money > this.balance[account1 - 1]
)
return false;
this.balance[account1 - 1] -= money;
this.balance[account2 - 1] += money;
return true;
}
deposit(account: number, money: number): boolean {
if (account > this.balance.length) return false;
this.balance[account - 1] += money;
return true;
}
withdraw(account: number, money: number): boolean {
if (account > this.balance.length || money > this.balance[account - 1]) {
return false;
}
this.balance[account - 1] -= money;
return true;
}
}
/**
* Your Bank object will be instantiated and called as such:
* var obj = new Bank(balance)
* var param_1 = obj.transfer(account1,account2,money)
* var param_2 = obj.deposit(account,money)
* var param_3 = obj.withdraw(account,money)
*/type Bank struct {
balance []int64
n int
}
func Constructor(balance []int64) Bank {
return Bank{balance, len(balance)}
}
func (this *Bank) Transfer(account1 int, account2 int, money int64) bool {
if account1 > this.n || account2 > this.n || this.balance[account1-1] < money {
return false
}
this.balance[account1-1] -= money
this.balance[account2-1] += money
return true
}
func (this *Bank) Deposit(account int, money int64) bool {
if account > this.n {
return false
}
this.balance[account-1] += money
return true
}
func (this *Bank) Withdraw(account int, money int64) bool {
if account > this.n || this.balance[account-1] < money {
return false
}
this.balance[account-1] -= money
return true
}
/**
* Your Bank object will be instantiated and called as such:
* obj := Constructor(balance);
* param_1 := obj.Transfer(account1,account2,money);
* param_2 := obj.Deposit(account,money);
* param_3 := obj.Withdraw(account,money);
*/struct Bank {
balance: Vec<i64>,
}
/**
* `&self` means the method takes an immutable reference.
* If you need a mutable reference, change it to `&mut self` instead.
*/
impl Bank {
fn new(balance: Vec<i64>) -> Self {
Bank { balance }
}
fn transfer(&mut self, account1: i32, account2: i32, money: i64) -> bool {
let (account1, account2, n) = (account1 as usize, account2 as usize, self.balance.len());
if n < account1 || n < account2 {
return false;
}
if self.balance[account1 - 1] < money {
return false;
}
self.balance[account1 - 1] -= money;
self.balance[account2 - 1] += money;
true
}
fn deposit(&mut self, account: i32, money: i64) -> bool {
let (account, n) = (account as usize, self.balance.len());
if n < account {
return false;
}
self.balance[account - 1] += money;
true
}
fn withdraw(&mut self, account: i32, money: i64) -> bool {
let (account, n) = (account as usize, self.balance.len());
if n < account {
return false;
}
if self.balance[account - 1] < money {
return false;
}
self.balance[account - 1] -= money;
true
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Direct Array Manipulation | Time Complexity: O(1), constant time operations per method. |
| HashMap for Account Balances | Time Complexity: O(1) on average due to hash table efficiency. |
| Simulation | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Direct Array Manipulation | O(1) per operation | O(n) | When account numbers map directly to indices and the total number of accounts is fixed |
| HashMap for Account Balances | O(1) average per operation | O(n) | When account identifiers may be sparse, dynamic, or not aligned with array indices |
Frequently asked questions
Is Simple Bank System easy or hard?
The problem is rated Medium because it focuses on system design and edge-case handling rather than complex algorithms. The challenge comes from validating accounts, ensuring correct balance updates, and implementing all operations cleanly with constant-time performance.
Simple Bank System Python/Java solution
In Python or Java, the typical implementation stores balances in an array or list and exposes methods for transfer, deposit, and withdraw. Each method checks account validity, verifies sufficient funds when needed, and updates balances in constant time.
How to solve Simple Bank System in O(n)?
The overall design uses O(n) space to store balances, but each operation runs in O(1) time. Maintain an array or HashMap of balances, validate account indices or keys, check available funds for withdrawals or transfers, and update balances directly.
What is the best approach for Simple Bank System?
The direct array approach is usually the best solution. Account numbers map directly to array indices, allowing constant-time access for transfer, deposit, and withdrawal operations. Each operation performs simple checks and updates in O(1) time with O(n) space for storing balances.
Is Simple Bank System asked at Google/Amazon/Meta?
Problems involving banking operations and system simulations appear frequently in interviews at large tech companies. While this exact question may vary, the underlying concepts—designing operations with constant-time updates and validating state—are common in coding interviews.
What data structure is used in Simple Bank System?
The main data structures are arrays and hash tables. Arrays provide constant-time index access when account numbers are sequential, while HashMaps allow flexible mapping between account IDs and balances when identifiers are sparse or dynamic.
What is the time complexity of Simple Bank System?
Each operation—transfer, deposit, and withdraw—runs in O(1) time because it only accesses or updates a few stored balances. The system stores all balances in either an array or a HashMap, resulting in O(n) space complexity where n is the number of accounts.
