Search
⌘K

Leetcode 2043. Simple Bank System

Asked at:

Capital One

PayPal

Coinbase


DESCRIPTION

Design a Bank class that manages 1-indexed account balances and supports three core operations: transfer(account1, account2, money), deposit(account, money), and withdraw(account, money). Each operation must validate account indices and ensure sufficient funds for withdrawals/transfers, returning true on success (with balance updates) or false on failure. For example, if account 1 has $100 and you call transfer(1, 2, 50), account 1 should have $50 and account 2 should have $50 more.

Input:

Bank(2)
deposit(1, 100)
withdraw(1, 50)
transfer(1, 2, 30)

Output:

true
true
true


Explanation: Initialize 2 accounts. Deposit $100 to account 1 (balance: $100). Withdraw $50 from account 1 (balance: $50). Transfer $30 from account 1 to account 2 (balances: account 1 = $20, account 2 = $30).

Constraints:

  • 1 <= n <= 10^5 (number of accounts)
  • 1 <= account, account1, account2 <= n (valid account indices)
  • 1 <= money <= 10^14 (transaction amount)
  • At most 10^4 calls to transfer, deposit, and withdraw
  • Accounts are 1-indexed (first account is index 1, not 0)

Understanding the Problem

The core challenge is implementing a stateful banking system that tracks multiple account balances while enforcing validation rules on every operation. You must handle 1-indexed accounts (a common source of off-by-one errors) and ensure atomic operations where invalid transactions don't partially modify state. The key design decision is choosing an appropriate data structure to store balances efficiently while supporting O(1) lookups and updates.

Building Intuition

A naive approach might use a dictionary with account numbers as keys, but this wastes space for dense account ranges. A better approach uses an array/list to store balances, mapping 1-indexed accounts to 0-indexed array positions (balance[account - 1]). For example, if n=3, initialize [0, 0, 0] and access account 2's balance via balance[1].

This pattern appears in financial systems, resource allocation, and inventory management where you need to track multiple entities with unique identifiers. Understanding index mapping (1-indexed to 0-indexed) and validation-before-mutation prevents critical bugs in production systems. The design also teaches encapsulation by hiding internal array indexing from the public API.

Common Pitfalls

Implementation

Class Initialization and Balance Storage

Initialize the Bank class with n accounts, storing balances in a 0-indexed array of size n (all starting at $0). The constructor maps 1-indexed account numbers to 0-indexed array positions by subtracting 1. For example, Bank(3) creates balance = [0, 0, 0] where account 1's balance is at balance[0]. This provides O(1) access for all operations while maintaining the 1-indexed public API.

Solution
class Bank:
def __init__(self, balance: list[int]):
"""
Initialize bank with n accounts (1-indexed API, 0-indexed storage).
:param balance: List of initial balances for accounts 1 to n
"""
self.balance = balance[:]
self.n = len(balance)
Deposit and Withdraw Operations

Implement deposit(account, money) to add funds and withdraw(account, money) to remove funds with validation. Both must check if 1 <= account <= n (valid index) before accessing the array. Withdraw additionally checks balance[account - 1] >= money (sufficient funds) before deducting. For example, withdraw(2, 50) on balance = [100, 30, 0] fails (returns false) because account 2 only has $30.

Solution
def deposit(self, account: int, money: int) -> bool:
if 1 <= account <= self.n:
self.balance[account - 1] += money
return True
return False
def withdraw(self, account: int, money: int) -> bool:
if 1 <= account <= self.n and self.balance[account - 1] >= money:
self.balance[account - 1] -= money
return True
return False
Transfer Operation with Dual Validation

Implement transfer(account1, account2, money) by validating both accounts exist and source has sufficient funds before modifying any state. The operation is atomic: either both balances update or neither does. For example, transfer(1, 4, 50) with n=3 returns false (account 4 doesn't exist) without touching account 1's balance. Only after all checks pass do you execute balance[account1 - 1] -= money and balance[account2 - 1] += money.

Solution
def transfer(self, account1: int, account2: int, money: int) -> bool:
# Validate both accounts exist (1-indexed)
if account1 < 1 or account1 > len(self.balance):
return False
if account2 < 1 or account2 > len(self.balance):
return False
# Validate source has sufficient funds
if self.balance[account1 - 1] < money:
return False
# Atomic update: both balances change together
self.balance[account1 - 1] -= money
self.balance[account2 - 1] += money
return True

What We've Learned

  • Pattern: Use array indexing with offset (`array[id - 1]`) to map 1-indexed IDs to 0-indexed storage efficiently
  • Validation: Always validate before mutate to ensure atomic operations and prevent invalid state transitions
  • Use Case: Applies to account systems, resource pools, and entity managers where IDs start at 1 but internal storage is 0-indexed

Problems to Practice

Overview
Lesson
Two Pointers

Related lesson that helps practice similar concepts and patterns.

Container With Most Water

medium

Two Pointers

Related problem that helps practice similar concepts and patterns.

Question Timeline

See when this question was last asked and where, including any notes left by other candidates.

Late February, 2026

Capital One

Senior

Late January, 2026

Capital One

Senior

Mid December, 2025

Capital One

Senior

Comments

Your account is free and you can post anonymously if you choose.