恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
LogicStack-LeetCode 刷题笔记:LeetCode 2043 简易银行系统(中等)——模拟类设计题的完整题解与多语言实现
首页
资讯中心
/
LogicStack-LeetCode 刷题笔记:LeetCode 2043 简易银行系统(中等)——模拟类设计题的完整题解与多语言实现
LogicStack-LeetCode 刷题笔记:LeetCode 2043 简易银行系统(中等)——模拟类设计题的完整题解与多语言实现
发布时间:2026/10/9 1:47:55
教程文档【免费下载链接】LogicStack-LeetCode公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码项目地址https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode点击查看免费下载本篇以 LogicStack-LeetCode 仓库中 LeetCode/2041-2050/2043. 简易银行系统中等.md 的官方题解为骨架围绕「模拟」这一核心 Tag 展开。你将掌握这类「设计类 规则模拟」题目的通用解题套路如何把题面中的交易规则翻译成精确的代码判断如何做账户编号到数组下标的映射以及为什么余额必须使用long而非int。读完即可独立复现并提交本题并能在后续同类模拟题中复用这套方法论。题目描述这是 LeetCode 第 2043 题《简易银行系统》Simple Bank System难度为中等Tag 为「模拟」。任务是为一款银行设计程序自动化执行所有传入的交易转账、存款和取款。银行共有 $n$ 个账户编号从 $1$ 到 $n$。每个账户的初始余额存储在一个下标从 $0$ 开始的整数数组balance中其中第 $(i 1)$ 个账户的初始余额是balance[i]。所有交易必须有效才会被执行。交易有效需要同时满足下面两个条件指定的账户数量在 $1$ 和 $n$ 之间取款或者转账所需的钱的总数小于等于账户余额。需要实现Bank类共四个方法方法签名行为Bank(long[] balance)使用下标从 $0$ 开始的整数数组balance初始化该对象boolean transfer(int account1, int account2, long money)从编号account1的账户向编号account2的账户转账money美元成功返回true否则返回falseboolean deposit(int account, long money)向编号account的账户存款money美元成功返回true否则返回falseboolean withdraw(int account, long money)从编号account的账户取款money美元成功返回true否则返回false示例解析与数据范围题目给出的示例输入 [Bank, withdraw, transfer, deposit, transfer, withdraw] [[[10, 100, 20, 50, 30]], [3, 10], [5, 1, 20], [5, 20], [3, 4, 15], [10, 50]] 输出 [null, true, true, true, false, false]逐步推演如下Bank bank new Bank([10, 100, 20, 50, 30])初始化 5 个账户余额分别为 $10、100、20、50、30$。bank.withdraw(3, 10)返回true。账户 3 余额为 $20 \ge 10$可以取款 $10$余额变为 $20 - 10 10$。bank.transfer(5, 1, 20)返回true。账户 5 余额为 $30 \ge 20$可以转账。账户 5 余额变为 $30 - 20 10$账户 1 余额变为 $10 20 30$。bank.deposit(5, 20)返回true。账户 5 存款 $20$余额变为 $10 20 30$。bank.transfer(3, 4, 15)返回false。账户 3 当前余额只有 $10 15$余额不足无法转账 $15$。bank.withdraw(10, 50)返回false。账户 10 不存在$n 5$交易无效。数据范围提示是本题选择数据结构与数值类型的关键依据$n balance.length$$1 \le n,\ account,\ account_1,\ account_2 \le 10^5$$0 \le balance[i],\ money \le 10^{12}$transfer、deposit、withdraw三个函数各自最多被调用 $10^4$ 次思路分析把「规则」翻译成代码本题没有任何隐藏技巧题解给出的核心思路只有一句话根据题意进行模拟即可。真正考验的是两点第一正确拆解「有效交易」的两条规则。规则一针对账户编号规则二针对金额。注意两条规则的适用对象并不完全相同deposit存款只涉及一个账户因此只需校验「账户存在」这一条规则不需要校验余额——存款不存在余额不足的问题withdraw取款需要同时校验「账户存在」和「余额充足」transfer转账涉及两个账户需要两个账户都存在缺一不可并且转出方余额充足。第二正确建立「账户编号」与「数组下标」的映射。题面规定账户编号从 $1$ 开始而balance数组下标从 $0$ 开始因此编号为account的账户对应数组下标account - 1。这是最容易写错的地方若直接使用val[account]访问编号 $1$ 号账户会被错误地映射到下标 $1$即第 $2$ 个账户而编号 $n$ 的账户访问val[n]会直接越界。在原题解中这两点被浓缩为一个check(int account)辅助函数boolean check(int account) { return 1 account account val.length; }check一次性完成了「编号下限 $1$」和「编号上限 $n$」的双重校验三个交易方法都能复用。Java 参考实现含逐行注释以下是原题解中给出的 Java 参考代码保留了其简洁风格并补充了注释class Bank { long[] val; // 账户余额数组val[i] 表示编号为 (i 1) 的账户余额 // 初始化直接持有 balance 数组的引用不额外拷贝 public Bank(long[] balance) { val balance; } // 校验账户编号是否合法编号范围 [1, val.length] boolean check(int account) { return 1 account account val.length; } // 转账从账户 a 向账户 b 转 c 美元 public boolean transfer(int a, int b, long c) { // 规则一两个账户都必须存在 if (!check(a) || !check(b)) return false; // 规则二转出方余额必须充足余额恰好等于金额时也允许 if (val[a - 1] c) { val[a - 1] - c; // 转出方扣款 val[b - 1] c; // 转入方入账 return true; } return false; } // 存款向账户 a 存入 c 美元只校验账户存在无需校验余额 public boolean deposit(int a, long c) { if (!check(a)) return false; val[a - 1] c; return true; } // 取款从账户 a 取出 c 美元 public boolean withdraw(int a, long c) { if (!check(a)) return false; // 余额必须充足 if (val[a - 1] c) { val[a - 1] - c; return true; } return false; } }实现细节与边界情况剖析这一节把参考实现中容易被忽略的细节逐个拆开它们是本题通过率的关键。1. 为什么要用long而不是int这是本题最重要的数据范围陷阱。balance[i]和money的上限都是 $10^{12}$而int的最大值约为 $2.1 \times 10^9$单笔金额就已经超出int范围更不用说累加后的余额。用long之后是否仍然安全可以做一次上界估算单个账户的最大余额初始 $10^{12}$之后每次操作最多增加 $10^{12}$存款或转入操作次数上限 $10^4$ 次因此单账户余额上界约为 $10^{12} 10^4 \times 10^{12} \approx 10^{16}$全部账户余额总和不变转账只是余额在两账户间流动上界为 $n \times 10^{12} \le 10^5 \times 10^{12} 10^{17}$。两者都远小于long的上限 $2^{63} - 1 \approx 9.2 \times 10^{18}$因此使用long在整个数据范围内都不会溢出。这也解释了题解中所有方法签名都使用long的原因。2.transfer的校验顺序先查账户再查余额。参考实现严格遵循「先check(a) || check(b)再判断val[a-1] c」的顺序。这个顺序很重要如果先访问val[a-1]再校验编号遇到非法账户如示例中的账户 10就会产生数组越界。先做存在性校验可以同时保证访问安全性这也是check被设计为独立函数的原因。3. 余额比较用而非。题面规定「取款或者转账需要的钱的总数小于或者等于账户余额」即为有效因此余额恰好等于金额时也允许交易代码中使用。4. 存款为何不需要余额判断存款只会增加余额不存在「余额不足」的可能因此deposit只需通过check校验账户存在性即可这也是它与withdraw在结构上对称、在判断上不对称的原因。5. 自转账account1 account2的边界。若两个参数传入同一账户逻辑上「先扣款再入账」会先减后加、净额为零只要余额充足即可返回true实现无需特判。当然在实际银行系统中自转账通常会被业务层拦截但按本题规则它是允许的。6. 初始化时直接持有引用。Bank(long[] balance)直接把数组引用赋给成员变量val没有拷贝。因为后续所有操作都发生在val上这样既简洁又不影响正确性若担心外部修改可以复制一份balance.clone()但按题目约定传入的数组只用于初始化直接持有引用即可。其他语言的等价实现原题解仅给出 Java 代码。按照同一套模拟逻辑可以给出以下等价实现与 38. 外观数列、2069. 模拟行走机器人 II 等文章的多语言风格保持一致供不同语言栈的读者提交参考。C 实现class Bank { public: vectorlong long val; Bank(vectorlong long balance) { val balance; } bool check(int account) { return 1 account account (int)val.size(); } bool transfer(int a, int b, long long c) { if (!check(a) || !check(b)) return false; if (val[a - 1] c) { val[a - 1] - c; val[b - 1] c; return true; } return false; } bool deposit(int a, long long c) { if (!check(a)) return false; val[a - 1] c; return true; } bool withdraw(int a, long long c) { if (!check(a)) return false; if (val[a - 1] c) { val[a - 1] - c; return true; } return false; } };Python 实现class Bank: def __init__(self, balance: List[int]): self.val balance def check(self, account: int) - bool: return 1 account len(self.val) def transfer(self, account1: int, account2: int, money: int) - bool: if not self.check(account1) or not self.check(account2): return False if self.val[account1 - 1] money: self.val[account1 - 1] - money self.val[account2 - 1] money return True return False def deposit(self, account: int, money: int) - bool: if not self.check(account): return False self.val[account - 1] money return True def withdraw(self, account: int, money: int) - bool: if not self.check(account): return False if self.val[account - 1] money: self.val[account - 1] - money return True return FalsePython 的整数是任意精度类型天然不存在溢出问题直接使用int即可。TypeScript 实现class Bank { private val: number[]; constructor(balance: number[]) { this.val balance; } private check(account: number): boolean { return 1 account account this.val.length; } transfer(account1: number, account2: number, money: number): boolean { if (!this.check(account1) || !this.check(account2)) return false; if (this.val[account1 - 1] money) { this.val[account1 - 1] - money; this.val[account2 - 1] money; return true; } return false; } deposit(account: number, money: number): boolean { if (!this.check(account)) return false; this.val[account - 1] money; return true; } withdraw(account: number, money: number): boolean { if (!this.check(account)) return false; if (this.val[account - 1] money) { this.val[account - 1] - money; return true; } return false; } }需要说明的是TypeScript / JavaScript 的number是 IEEE 754 双精度浮点其安全整数上限为 $2^{53} \approx 9.0 \times 10^{15}$而本题单账户余额在极端数据下可逼近 $10^{16}$理论上存在精度风险常规测试数据下number即可通过若追求绝对安全可以改用BigInt。复杂度分析时间复杂度$O(1)$。transfer、deposit、withdraw三个方法均只涉及常数次数组读写与比较不随账户数 $n$ 或操作次数增长。初始化Bank构造器为 $O(n)$数组引用赋值本身是 $O(1)$但传入的数组本身长度为 $n$。空间复杂度$O(n)$。需要存储长度为 $n$ 的余额数组val。综合来看三类操作各最多调用 $10^4$ 次总时间复杂度为 $O(10^4)$ 级别远在题目限制之内。仓库中的延伸阅读模拟题方法论本题在 LogicStack-LeetCode 仓库中被归类为「模拟」可以在 Index/模拟.md 中找到完整的模拟题索引仓库 README.md 对该系列的整体定位是「日更」的算法刷题仓库每篇题解按 Tag 分类归档。在模拟索引中本题No.2043的推荐指数为 属于值得反复练习的经典模拟题。把本题与仓库中的其他模拟题对照可以提炼出模拟类题目的通用方法论精读题面逐条列出规则。本题的规则只有「账户存在」和「余额充足」两条很多同学出错是因为把规则想复杂了比如给存款也加了余额判断。先做存在性/合法性校验再访问数据。本题的check函数先于一切数组访问执行杜绝越界66. 加一 中则表现为对进位t的循环终止条件i 0 || t ! 0的兜底处理。把状态维护在简单的数据结构里。本题直接用数组存余额即可无需哈希表2069. 模拟行走机器人 II 则用单个步数变量loc加取模维护外圈位置同样是「最简单结构 规则分情况」的组合。对数据范围保持敏感选对数值类型。本题long的选用、38. 外观数列 中对 $n \le 30$ 使用打表优化都是「数据范围决定实现策略」的体现。此外转账的「先扣款后入账」与 2. 两数相加 中「逐位相加并维护进位」同属对运算过程的忠实模拟——区别仅在于本题的运算发生在账户余额上而后者发生在十进制数位上。掌握本题后遇到任何「按规则执行交易/操作并返回结果」的设计题都可以沿用「合法性校验 → 状态更新 → 返回结果」三段式结构快速求解。赞分享教程文档【免费下载链接】LogicStack-LeetCode公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码项目地址https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode点击查看免费下载相关推荐LeetCode 1773 统计匹配检索规则的物品数量模拟解法与多语言实现LogicStack-LeetCode 刷题笔记LeetCode 1773 统计匹配检索规则的物品数量模拟解法与多语言实现LogicStack LeetCode 刷题笔记 本篇技术指南以 LogicSt教程文档LeetCode 1047 题解删除字符串中的所有相邻重复项——栈与数组模拟的多种实现LogicStack-LeetCode 刷题笔记LeetCode 1047 题解删除字符串中的所有相邻重复项——栈与数组模拟的多种实现LogicStack LeetCode 刷题笔记 导读 本文围绕 L教程文档LeetCode 1669 合并两个链表中等题解区间断链与整链拼接的链表模拟实战LogicStack-LeetCode 刷题日记LeetCode 1669 合并两个链表中等题解区间断链与整链拼接的链表模拟实战LogicStack LeetCode 刷题日记 本篇技术指南以「宫水教程文档上一篇APISIX Stream 代理TCP/UDP 动态代理实战指南配置、路由匹配与 TLS/PROXY 协议下一篇如何控制 OpenSRE 的 Token 成本/cost 每会话成本追踪实用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考