开头先啰嗦一句:如果你的刷题列表里躺着这道题,你可能觉得它简单到不值得写笔记。但等你真正在面试白板前写过一遍,或者帮人 review 过代码,你就会发现,越是这种看起来人畜无害的题,越容易在细节上翻车。“加一”这题我前前后后遇到过三次翻车现场:一次是新手直接用 int(''.join(...)) 转数字加一再转回数组,结果被大数溢出按在地上摩擦;一次是没考虑 999 这种全是 9 的输入,返回了 [0, 0, 0];还有一次是陷入了“要不要原地修改”的纠结,把简单问题复杂化。所以这次把题目的完整笔记整理出来,从题目本质、解法演变、边界条件到面试考察逻辑都梳理一遍,希望能帮你把这道题彻底吃透。
1. 题目本身很简单,为什么值得专门写一篇笔记
很多刷题的人有个误区,觉得简单题就是送分题,看一眼答案就划走。但实际上面试里遇到的中低难度题,恰恰是区分“背过答案”和“真懂原理”的分水岭。加一这道题,LeetCode 上是 Easy 难度,通过率却一直在 45% 上下徘徊,说明有一半的人第一次提交是错的。这个数据本身就说明问题了。
1.1 题目描述与本质拆解
原题说的是:给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位,数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
举个例子:
- 输入
[1, 2, 3],代表数字 123,加一后得到 124,输出[1, 2, 4] - 输入
[4, 3, 2, 1],代表数字 4321,加一后得到 4322,输出[4, 3, 2, 2] - 输入
[9],代表数字 9,加一后得到 10,输出[1, 0]
表面上看,这就是个“小学数学加法”的数组模拟。但它的本质其实是一个数字进位与存储结构转换的问题:你把一个整数按十进制位拆开,存进一个数组,然后对这个“拆开后的数字”执行一次加一操作,最后还要以同样的结构返回结果。
这里面的核心矛盾在于:数组的每一位都是独立的,而加法偏偏是带进位的。进位一旦产生,就会向高位传播,传播到最高位时还会导致数组长度增加。这个“长度变化”是所有新手最容易忽略的地方。
1.2 题目常见的三个翻车点
我见过最多的错误有三个:
第一,试图用语言自带的数字类型直接转换。比如 Python 里写 str(int(''.join(map(str, digits))) + 1)。这个写法在数字比较小的时候没问题,但题目并没有限制数组长度,一旦数组有几十位甚至上百位,int() 转换出来的数字早超过语言支持的整数范围了。Python 的 int 虽然理论上不限长度,但很多其他语言(比如 Java 的 int、C++ 的 long long)会直接溢出,这种行为本身就是面试官最不想看到的——说明你根本没有考虑数据规模。
第二,没有处理“全是 9”的情况。比如输入 [9, 9, 9],加一后应该是 [1, 0, 0, 0],数组长度从 3 变成了 4。有人写循环的时候只改了末尾的 9,没处理最高位的进位,最后返回 [0, 0, 0] 或者 [0, 0, 0, 0],完全不对。
第三,纠结于“原地修改”。题目没有说不能新建数组,但很多人看到“数组”就默认要原地操作,结果为了原地修改写了一堆复杂的指针移动,反而把自己绕晕了。其实这个题在原地修改和新建数组之间,时间复杂度都是 O(n),空间复杂度最坏情况下也只是 O(1) 或 O(n) 的区别,完全没必要为了“看起来更优”而牺牲代码清晰度。
所以这道题的价值不在于“难”,而在于它在很小的体量里,把进位传播、边界处理、数组与数字的映射关系全都考了一遍。它不是那种看一眼就知道答案的题,而是需要你在心里走一遍加法运算的完整过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心思路:从小学数学出发理解进位机制
先说结论:这道题的最优解法,时间复杂度是 O(n),空间复杂度是 O(1)(不考虑返回数组占用的空间)。思路简单到让人怀疑人生——从数组最后一位开始,模拟一次“加一”的竖式加法过程,唯一需要处理的就是“逢十进一”的传播。
但“简单”不等于“理所当然”。我在复盘的时候发现,真正把这道题吃透的人,都知道三条隐藏的观察结论,这三条结论才是写出优雅代码的基础。
2.1 观察一:加一操作只会影响从末尾开始的一段连续 9
这个观察是整个题目的题眼。你想象一下一个数在十进制下长什么样,比如 12345,加一之后变成 12346,只有最后一位变了。如果末尾是 1239,加一后变成 1240,影响到了倒数第二位。如果末尾是 1299,加一后变成 1300。如果是 9999,加一后变成 10000,每一位都变了,而且数组长度还多了一位。
也就是说,进位是从低位向高位逐位传播的,而且一旦某一位加一后不等于 10,进位链就断了,更高位完全不需要动。这就是“从后往前遍历,遇到不是 9 就停”这个策略的依据。
有些题解会把这个问题说成“找到最右边那个不是 9 的位置”,我觉得
