Appearance
实战:加一
这是一道模拟题,考察的是进位处理的逻辑。
看似简单,但有一个容易忽略的边界情况。
题目描述
LeetCode 66. 加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位,数组中每个元素只存储单个数字。
示例 1:
输入:digits = [1, 2, 3]
输出:[1, 2, 4]
解释:输入数组表示数字 123,加一后为 124示例 2:
输入:digits = [9, 9, 9]
输出:[1, 0, 0, 0]
解释:输入数组表示数字 999,加一后为 1000题目分析
模拟我们手算加法的过程:
- 从最低位(数组末尾)开始
- 加一
- 如果有进位,继续处理前一位
关键问题:什么时候会产生进位?
当这一位是 9 时,加一变成 10,需要进位。
解法:模拟进位
javascript
function plusOne(digits) {
const n = digits.length;
// 从末尾开始处理
for (let i = n - 1; i >= 0; i--) {
if (digits[i] < 9) {
// 不需要进位,直接加一返回
digits[i]++;
return digits;
}
// 当前位是 9,变成 0,继续处理前一位
digits[i] = 0;
}
// 所有位都是 9,需要在最前面加 1
// 例如 [9, 9, 9] -> [1, 0, 0, 0]
return [1, ...digits];
}代码很简洁。核心逻辑是:
- 不是 9:直接加一,结束
- 是 9:变成 0,处理前一位(相当于进位)
- 全是 9:所有位都变成 0,在最前面加个 1
执行过程
Case 1:[1, 2, 3]
i=2: digits[2]=3 < 9, digits[2]=4
return [1, 2, 4]Case 2:[1, 2, 9]
i=2: digits[2]=9, digits[2]=0
i=1: digits[1]=2 < 9, digits[1]=3
return [1, 3, 0]Case 3:[9, 9, 9]
i=2: digits[2]=9, digits[2]=0
i=1: digits[1]=9, digits[1]=0
i=0: digits[0]=9, digits[0]=0
循环结束,digits = [0, 0, 0]
return [1, 0, 0, 0]复杂度分析:
- 时间复杂度:O(n)——最坏情况遍历所有位
- 空间复杂度:O(1)——原地修改(全 9 情况除外)
为什么不直接转数字?
你可能会想:把数组转成数字,加一,再转回数组,不是更简单吗?
javascript
// 错误做法
function plusOne(digits) {
const num = parseInt(digits.join('')) + 1;
return String(num).split('').map(Number);
}问题:大数溢出。
JavaScript 的 Number 类型最大安全整数是 2^53 - 1(约 9 × 10^15)。如果输入的数组表示的数字超过这个值,就会出错。
数组模拟加法可以处理任意大的数字,这也是这道题的考点之一。
常见错误
- 忘记处理全 9 情况
javascript
// 错误:没有处理全 9
function plusOne(digits) {
for (let i = digits.length - 1; i >= 0; i--) {
if (digits[i] < 9) {
digits[i]++;
return digits;
}
digits[i] = 0;
}
// 忘记处理全 9,返回 [0, 0, 0]
return digits;
}- 从左往右遍历
加法要从低位开始,也就是数组的末尾。从左往右是错的。
相关题目
这道题是"大数加法"的简化版。类似的题目还有:
- 67. 二进制求和:二进制加法
- 415. 字符串相加:两个字符串表示的数相加
- 2. 两数相加:链表表示的数相加
它们的核心都是模拟进位。
本章小结
这一章我们学习了进位处理的基本逻辑:
- 从低位到高位处理
- 小于 9:直接加一,结束
- 等于 9:变成 0,继续处理前一位
- 全是 9:需要在最前面加 1
这个模式在很多大数运算的题目中都会用到。
下一章,我们来看一道需要发现规律的题目:「找到所有数组中消失的数字」。