LeetCode 1431:先找最大值再逐个比较,手撕数组遍历入门题

发布时间:2026/10/7 16:52:57
LeetCode 1431:先找最大值再逐个比较,手撕数组遍历入门题 1. 题目到底在说什么先别急着敲代码很多基础差的朋友一看到 LeetCode 英文题面就慌其实 1431 这道题翻译成大白话就是有一群小朋友手里各捧着一堆糖果现在你手里还有一堆额外的糖果extraCandies你挨个问每个小朋友“我把这些额外糖果全给你你能成为全班糖果最多的人吗”能就返回 true不能就返回 false最后把所有人的答案拼成一个列表。我先给你把原题还原一下。题目给了你一个数组 candies数组里每个数字代表一个小朋友当前拥有的糖果数比如 candies [2,3,5,1,3] 就是 5 个小朋友分别有 2、3、5、1、3 颗糖。另外给一个整数 extraCandies表示你额外拥有的糖果数。要求返回一个 List 第 i 个元素代表如果把 extraCandies 全部分给第 i 个小朋友他能不能成为拥有糖果最多的人。这道题的难点不在于算法有多高级而在于很多人做题时容易“审题不细”。题目里说的是“成为拥有糖果最多的人”不是“并列最多也可以”而是只要能到达或者超过当前所有人里的最大值就行。比如当前最大值是 5你给某个小朋友加了额外糖果后总数正好等于 5那也是 true因为他并列第一也是最多。这一点在写判断条件的时候特别容易写错很多人下意识写成“大于最大值”结果直接挂掉一个测试用例。这道题出现在 LeetCode 的“经典面试 75 题”里属于数组遍历类的入门题。大厂笔试、机考时经常拿这种题来考察最基础的编码能力——你连遍历数组、求最大值、条件判断这种基本功都不扎实后面更复杂的题目根本没法看。所以我非常建议基础薄弱的朋友从这类题开始手撕把一道简单题吃透比囫囵吞枣刷十道难题有用得多。2. 算法思路拆解一个“先找天花板再逐个比”的笨办法2.1 核心思路先找最大值再跟每个人去比这道题的解法思路非常直白归纳起来就是两步第一步遍历整个 candies 数组找到当前所有小朋友中拥有糖果数量的最大值 maxCandies。这个最大值就是“天花板”是后面每个小朋友都要去够的标杆。第二步再次遍历数组对每个小朋友用他当前的糖果数加上 extraCandies如果加完之后的结果大于等于 maxCandies就把 true 放进答案列表否则放 false。你可能会问为什么要先找最大值我能不能一边遍历一边判断不能因为你判断第一个人能不能成为最多的时候你根本不知道后面有没有人糖果数更高。如果后面有个人糖果数比前面所有人都高那你前面判断的结果就是错的。所以必须先完整地看一遍知道最高值是多少才能做比较。这个思路在算法里有个经典的名字叫“两次遍历”时间复杂度是 O(n)空间复杂度是 O(1)不计输出数组的话。对于这道题的数据规模candies 的长度最多也就 100 左右这种方案完完全全够用不需要任何花哨的优化。2.2 为什么说这个“笨办法”反而最优可能有人会想能不能排序能不能用贪心我告诉你在这道题里两次遍历就是最优解。排序的话你确实能快速知道最大值是谁但你排序之后数组下标就变了你还要额外维护每个小朋友原来的位置不然最后返回的答案就跟原顺序对不上了反而多此一举。用贪心思想虽然也能解释得通但本质上你仍然需要先知道全局最大值所以绕不开第一次遍历。我之前见过有些同学一上来就想着用 Python 里的 max() 函数这当然可以而且 Python 里 max(candies) 本质上也是在做一次 O(n) 的遍历所以用不用内置函数都不影响时间复杂度的级别。一句话总结这个思路先找最大再逐个比大小比得过就 true比不过就 false。这是我见过最容易理解、最不容易出错、也最适合基础差选手的解法。3. 手把手实现用你最熟悉的语言把代码敲出来3.1 基础版 Python 实现我先把最“朴素”的 Python 写法贴出来每一行我都给你嚼碎。from typing import List class Solution: def kidsWithCandies(self, candies: List[int], extraCandies: int) - List[bool]: # 第一步找到当前所有小朋友中拥有糖果数量的最大值 max_candies 0 for candy in candies: if candy max_candies: max_candies candy # 第二步逐个判断把结果存进列表 result [] for candy in candies: result.append(candy extraCandies max_candies) return result这段代码里最值得说的就是result.append(candy extraCandies max_candies)这一行。Python 里比较表达式会直接返回布尔值 True 或者 False所以你不需要先写 if 再 append True 再 else append False一行就能搞定。这在面试手撕代码时是加分项看起来干净利落。如果你想要更 Pythonic 的写法可以用列表推导式效果一模一样from typing import List class Solution: def kidsWithCandies(self, candies: List[int], extraCandies: int) - List[bool]: max_candies max(candies) return [candy extraCandies max_candies for candy in candies]用 max() 和列表推导式的版本代码量少了将近一半。但是我提醒一句如果你是第一次学这道题我建议你先用上面那个展开的写法把每一步循环和判断看清楚理解了之后再压缩成推导式。很多基础不好的同学直接背推导式结果面试时被面试官问一句“你解释一下这行为什么这么写”就卡住了。3.2 Java 和 C 版本面试时最常要求手写大厂机考和面试的时候很多面试官要求你用 Java 或者 C 写这里我把两种语言的标准答案也贴出来注解都给你写好。Java 版本class Solution { public ListBoolean kidsWithCandies(int[] candies, int extraCandies) { // 第一步找到最大值 int maxCandies 0; for (int candy : candies) { if (candy maxCandies) { maxCandies candy; } } // 第二步逐个判断 ListBoolean result new ArrayList(); for (int candy : candies) { result.add(candy extraCandies maxCandies); } return result; } }注意 Java 里candy extraCandies maxCandies这个表达式的结果也是 boolean 类型直接 add 到 List 里就可以了不需要额外做类型转换。C 版本class Solution { public: vectorbool kidsWithCandies(vectorint candies, int extraCandies) { int maxCandies 0; for (int candy : candies) { if (candy maxCandies) { maxCandies candy; } } vectorbool result; for (int candy : candies) { result.push_back(candy extraCandies maxCandies); } return result; } };你发现没有这三种语言的解题逻辑完全一样区别只是语法。所以我说基础差的人不要一开始就纠结“我该学哪种语言”你先拿你最熟悉的那门语言把思路理顺思路通了换语言只是换个写法而已。这也是我在标题里反复强调“手撕代码”的原因——面试官看的不是你会背哪道题而是你能不能把一个思路用代码完整地表达出来。3.3 手动模拟拿题目的例子走一遍流程光贴代码不够我带你手动模拟一遍确保你哪怕第一次接触这道题也能跟上。题目给的例子candies [2,3,5,1,3]extraCandies 3。第一步找最大值初始 max 0遇到 22 0max 2遇到 33 2max 3遇到 55 3max 5遇到 11 不超过 5max 不变遇到 33 不超过 5max 不变最终 max 5。第二步逐个判断第一个小朋友有 2 颗糖加上 3 颗额外糖果等于 55 5返回 true第二个小朋友有 3 颗糖加上 3 等于 66 5返回 true第三个小朋友有 5 颗糖加上 3 等于 88 5返回 true第四个小朋友有 1 颗糖加上 3 等于 44 5返回 false第五个小朋友有 3 颗糖加上 3 等于 66 5返回 true最终答案是 [true, true, true, false, true]和题目给出的预期输出完全一致。这个模拟过程你一定要自己在纸上写一遍。我教了这么多年刷题发现基础差的同学最容易犯的毛病就是“眼睛会了代码不会”看别人写觉得简单自己一写就各种报错。解决办法只有一个动手在草稿纸上把循环过程走一遍当你对每个变量在每个时刻的值都心里有数的时候代码自然就能写出来。4. 刷题过程中最容易踩的坑我替你先踩过了4.1 边界条件最大值初始化到底应该写多少很多新手第一次写这道题的时候会在最大值初始化这里翻车。有人写成max_candies candies[0]有人写成max_candies -1还有人写成max_candies 0。这几种写法在这道题里其实都能通过因为糖果数是非负整数但你得分清楚各自的适用场景。candies[0]这种写法适合数组一定非空的情况如果题目隐含条件说了长度至少为 1那就没问题。-1或者0适合糖果数可能为 0 的场景。我的建议是在没有明确说明数组非空的情况下用candies[0]要从数组下标 1 开始遍历或者直接遍历全部数组但用-1这种极小值做初始值最保险。因为这道题的糖果数量最小是 0-1必然会被第一个元素替换掉不会出错。另外这道题在 LeetCode 上给你的 candies 数组长度范围是 2 到 100所以实际上我们不用担心空数组的问题。但面试的时候有些面试官会故意问你“如果 candies 是空数组怎么办”你要能答上来空数组的话第一步找最大值会出问题但题目约束了长度至少为 2所以实际不会发生。至少说明你能意识到这个边界条件的存在这就已经比很多只会背答案的候选者强了。4.2 审题陷阱是“大于”还是“大于等于”我在前面已经提过一次这里再单独拉出来强调因为这个错真的太常见了。题目要求判断的是“能够拥有最多的糖果”也就是说如果加上额外糖果之后某个小朋友的糖果数恰好等于当前最大值那么他也是“最多”之一。所以判断条件必须是而不是。你想想生活中的场景全班最高分是 100 分你也考了 100 分老师和同学会不会说你是最高分当然会因为你们并列第一。这个道理放到糖果题里也是一样。如果写成那么所有原本就是最大值的小朋友加上额外糖果后虽然变得更大了但更关键的是那些恰好能追平的小朋友会被你错误地判成 false。这道题里有个经典测试用例结果分布得很均匀你写错一个运算符立刻就有测试用例过不去。4.3 Python 里用 list 复制踩过的坑有些同学写这道题的时候喜欢先用result [False] * len(candies)初始化一个列表然后在循环里根据条件修改对应位置的值。这个思路没问题[False] * len(candies)是常见的初始化手法。但要注意一种坑如果你写成result [False] * len(candies)之后再循环更新某个位置这是安全的因为这里的 False 是不可变对象。可如果你用类似的手段初始化一个二维列表比如[[False] * n] * m那就有大问题了因为外面那一层重复的是同一个列表对象的引用改一个位置别的行也跟着变。这道题虽然用不到二维列表但你刷 LeetCode 的过程中迟早会遇到这个坑。我见过不少人在二维数组题目上栽跟头就是因为不理解 Python 的引用机制。提前打个预防针等你在别的题里再碰到[[0] * n] * m这种初始化方式的时候能第一时间反应过来它是错的应该写成[[0] for _ in range(m)]。4.4 不要忽略 extraCandies 是 0 的情况还有一种极端情况容易被忽略extraCandies 0。这时候题目就退化成“每个小朋友本来是不是糖果最多的人”。按照我们的算法判断条件是candy 0 maxCandies也就是判断candy maxCandies。只有原本就是最大值的那几个小朋友会返回 true其他人都返回 false。这个逻辑是对的。但如果你审题不仔细搞反了判断方向比如你判断的是“给完别人之后我会不会依然最多”那题目就变味了代码也要完全重写。所以做这道题之前先花 30 秒问自己一句话到底是在比谁的糖果总数是比加了额外糖果后的人数答案清楚了再动手你会发现代码写起来一路畅通。很多同学刷题的时候有个坏习惯上来就写代码写到一半发现题目理解错了删了重来反复折腾。时间是次要的关键是心态容易崩。我的习惯是任何题目哪怕再简单先读题 30 秒用大白话把题目的要求说出来确认无误再动手。5. 从这道题延伸出去你能收获的不只是一个 return5.1 两次遍历思想的普适性这道题的核心思想“先找全局信息再逐个处理”在算法题里是个非常非常常见的套路。你以后会遇到很多问题本质上都是先遍历一遍收集信息再遍历一遍做判断或处理。举个典型的例子“商品价格追赶”类问题给一个数组表示每天的股票价格问你哪一天买入、哪一天卖出收益最大。最简单的暴力解法就是两层循环枚举所有买入卖出的组合O(n^2) 的复杂度。但理解了“先找全局信息”的思路之后你会发现可以维护一个“历史最低价格”在遍历过程中同时计算当天卖出的收益一遍遍历就能解决时间复杂度直接降为 O(n)。再比如“除自身以外数组的乘积”这道经典题要求输出一个数组每个位置的值是除了该位置以外所有元素的乘积。常规思路你先算出所有数的总乘积然后每个位置除以自身但这题有个附加限制不能用除法。这时候你就得用两次遍历第一次从左往右算前缀乘积第二次从右往左算后缀乘积巧妙地绕开除法。这个套路里“前缀后缀遍历”的思想和你今天学的“先找最大值再比较”其实是一脉相通的。5.2 怎么把一道简单题刷出面试价值很多基础差的朋友有个误区觉得简单题没营养要刷就刷难题。我的看法恰恰相反简单题才是地基。这道 1431 题虽然本身在大厂笔试里出现的概率不算特别高但它背后考察的遍历、求最大值、条件判断、返回列表这些是后面一百道题都要用到的基本功。我建议你刷这道题的时候额外做三件事第一用至少两种语言各写一遍。哪怕你找工作只准备 Java 或者只准备 Python我也建议你用两种语言实现。因为换语言的过程会逼你把思路和语法解耦你会发现“哦原来这个逻辑在 Java 里要这么写在 Python 里是那样写”这种对比能加深你对思路本身的理解。第二把这题改一改变成自己的练习题。比如不是给你额糖果而是问“如果必须把额外糖果分给某个人且必须让这个人独享最多不能并列该怎么判断”这时候判断条件就要从变回而且要先统计最大值出现了几次如果最大值出现了两次以上那无论你给谁加糖都没办法让他独享最多。这个小变体就是我 4.2 节提到的审题陷阱的进阶版你可以在 LeetCode 讨论区看到类似的问题变形。第三把时间复杂度和空间复杂度的分析写在代码注释里。很多面试官喜欢问“你这个算法复杂度是多少”这一题你如果脱口而出 O(n) 和 O(1)并且能解释清楚为什么第一遍遍历和第二遍遍历加起来还是 O(n)而不是 O(2n) 就是 O(n)面试官会觉得你的基本功很扎实。5.3 机考笔试的应试建议别在小题上丢分大厂机考和笔试尤其是海外大厂和国内头部互联网公司的在线评测通常不会只考一道简单题往往是一道简单加一道中等或者一道中等加一道困难。简单题存在的意义就是送分题用来筛掉那些连最基础的编码都写不利索的人。所以这种题目你不仅要写对还要写得快、写得稳。我给你的实操建议是拿到题目后先在注释里写下你的思路比如“1. 找最大值 2. 逐个判断”然后再开始写函数体。这样做的原因有两个。第一注释能帮助你理清思路不容易写到一半忘了下一步要干嘛。第二部分在线笔试平台会保存你的代码过程面试官后续看代码时能看到你的注释会觉得你是个有工程习惯的人而不是一个只会背答案的刷题机器。另外提醒一句机考环境里的代码编辑器往往没有本地 IDE 那么智能没有自动补全没有实时报错提示。所以你平时刷题时尽量别依赖 IDE 的自动补全练一练裸写代码的手感。这道 1431 题非常适合用来练习裸写——代码短、逻辑简单、变量名也好记多写几遍你就能习惯“没有提示也能把代码写对”的状态。6. 常见问题速查表刷题时对照着看我在带别人刷题和自己复盘的过程中整理了一份针对这道题的常见问题速查表你可以直接保存下来当作参考。问题类型症状原因解决办法结果全是 true 或全是 false输出没有区分度最大值找错了比如用初始值 0 且数组全为 0或判断条件写反打印 max_candies 检查确认判断方向是 candy extraCandies max边界用例报错最小值时答案不对extraCandies0 时判断逻辑出问题用额外糖果为 0 的例子手动走一遍编译报错返回值类型不对Python 里忘了加 List 类型标注、Java 里 ArrayList 没导入检查 import确认方法签名与题目要求一致运行超时复杂度过高用了两层循环或多次重复求 max只需要两次单层遍历一次找最大一次判断思路会写代码不会手写时变量名混乱缺乏一个固定的写题框架先写伪代码注释再填充真实代码这张表看着简单但都是我亲眼见过别人踩过、自己也踩过的坑。尤其是“结果全 true 或全 false”这个症状很多新手遇到时第一反应是怀疑自己的循环写错了其实往往只是最大值初始化或者判断方向出了问题。记住一个排查思路先确认数据再确认逻辑最后才怀疑语法。直接用print(max_candies)看看最大值是不是你预期的值这是最快定位问题的方式。7. 为什么我坚持让基础差的人“嚼碎了喂”说实话网上讲这道题的题解一抓一大把官方题解、各路大神的精讲、动画演示什么都有。但很多基础差的同学看这些题解依然觉得吃力原因是那些题解默认你已经有了一定的代码功底很多关键细节他们觉得“不用讲”可恰恰是这些细节卡住了新手。就拿这题的candy extraCandies max_candies来说熟练的开发者一眼就看明白甚至会觉得这行代码根本不需要解释。但对一个刚学会 for 循环、还没完全搞清楚布尔值是怎么回事的同学来说他会困惑为什么这个表达式可以直接加到列表里为什么它能等于 true这就是我反复强调“嚼碎代码”的原因——帮新手把每一个隐含的知识点都挖出来摆到明面上让他们不仅会抄还能吃透。我个人带过的刷题小组里很多同学第一周连 LeetCode 的输入输出格式都搞不明白但坚持每天手撕一道简单题、把每一行代码都能讲出个所以然来之后大概第四周左右他们再看中等难度的题目就已经不慌了。这道 1431 题就是他们最早手撕的那一批题目之一。所以如果你也是基础差的那一类别嫌弃这道题太简单它简单得恰到好处刚好能让你感受到“我能独立做出一道题”的成就感而这种成就感比任何技巧都更能支撑你走下去。