题目描述
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位,数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
思路及实现
方式一:反转数组后逐位相加
思路
首先,将数组反转,然后从最低位开始逐位相加。如果相加后的数值大于 9,则会产生进位,将进位保存到临时变量中。对于每一位的相加结果,直接赋值给反转后的对应位置,并且要注意处理进位。
完成所有位的相加后,检查最高位是否还有进位,如果有,则在数组最前面插入 1。
最后,再次反转数组,得到最终的结果。
代码实现
Java版本
class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;
// 反转数组
reverse(digits, 0, n - 1);
int carry = 1; // 初始化进位为 1
for (int i = 0; i < n; i++) {
int sum = digits[i] + carry;
digits[i] = sum % 10; // 取个位数
carry = sum / 10; // 更新进位
}
// 如果最高位还有进位,则在数组最前面插入 1
if (carry > 0) {
int[] newDigits = new int[n + 1];
newDigits[0] = carry;
System.arraycopy(digits, 0, newDigits, 1, n);
return newDigits;
}
// 再次反转数组得到最终结果
reverse(digits, 0, n - 1);
return digits;
}
// 反转数组指定区间内的元素
private void reverse(int[] digits, int start, int end) {
while (start < end) {
int temp = digits[start];
digits[start] = digits[end];
digits[end] = temp;
start++;
end--;
}
}
}
说明:
该实现首先将数组反转,方便从最低位开始逐位相加。使用
reverse
函数辅助反转数组指定区间的元素。完成逐位相加后,再次检查进位,并根据情况创建新数组或在原数组最前面插入 1。最后再次反转数组,得到最终的结果。
C语言版本
#include <stdlib.h>
#include <string.h>
void reverse(int *digits, int start, int end) {
while (start < end) {
int temp = digits[start];
digits[start] = digits[end];
digits[end] = temp;
start++;
end--;
}
}
int* plusOne(int* digits, int digitsSize, int* returnSize) {
int carry = 1;
int *result = (int *)malloc((digitsSize + 1) * sizeof(int));
if (!result) return NULL;
// 反转数组
reverse(digits, 0, digitsSize - 1);
// 逐位相加
for (int i = 0; i < digitsSize; i++) {
int sum = digits[i] + carry;
result[i] = sum % 10;
carry = sum / 10;
}
// 处理最高位进位
if (carry > 0) {
result[digitsSize] = carry;
*returnSize = digitsSize + 1;
} else {
*returnSize = digitsSize;
}
// 再次反转数组
reverse(result, 0, *returnSize - 1);
return result;
}
说明:
C语言版本与
思路及实现
方式二:从最高位开始逐位相加
思路
从数组的最高位(即数组末尾)开始逐位相加,如果当前位相加后的结果大于等于 10,则需要向高位进位。特别地,需要关注最高位是否有进位产生,如果有,则需要在数组最前面插入一个数字 1。
为了避免处理数组扩容问题,可以从数组末尾向前遍历,用一个变量来记录是否产生进位,并相应地更新数组元素。
代码实现
Java版本
class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;
int carry = 1; // 初始化进位为 1
for (int i = n - 1; i >= 0; i--) {
int sum = digits[i] + carry;
digits[i] = sum % 10; // 取个位数
carry = sum / 10; // 更新进位
if (carry == 0) {
// 如果进位为 0,则无需继续向前遍历
break;
}
}
// 如果最高位还有进位,则在数组最前面插入 1
if (carry > 0) {
int[] newDigits = new int[n + 1];
newDigits[0] = carry;
System.arraycopy(digits, 0, newDigits, 1, n);
return newDigits;
}
return digits;
}
}
说明:
该实现从数组末尾开始逐位相加,并更新进位。如果最高位还有进位,则创建新数组并在最前面插入 1。否则,直接返回原数组(因为数组长度可能不变)。
C语言版本
#include <stdlib.h>
int* plusOne(int* digits, int digitsSize, int* returnSize) {
int carry = 1;
int *result = (int *)malloc(digitsSize * sizeof(int));
if (!result) return NULL;
int i;
for (i = digitsSize - 1; i >= 0; i--) {
int sum = digits[i] + carry;
result[i] = sum % 10;
carry = sum / 10;
if (carry == 0) {
break;
}
}
if (carry > 0) {
// 如果最高位还有进位,则需要扩容数组
int *newResult = (int *)realloc(result, (digitsSize + 1) * sizeof(int));
if (!newResult) {
free(result);
return NULL;
}
result = newResult;
result[0] = carry;
*returnSize = digitsSize + 1;
} else {
*returnSize = i + 1; // 更新返回数组的长度
}
return result;
}
说明:
C语言版本的实现逻辑与 Java 版本相似,但需要注意内存管理。在可能需要扩容数组时,使用
realloc
函数重新分配内存,并检查是否分配成功。
Python3版本
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
carry = 1
n = len(digits)
for i in range(n - 1, -1, -1):
sum_val = digits[i] + carry
digits[i] = sum_val % 10
carry = sum_val // 10
if carry > 0:
digits.insert(0, carry)
return digits
说明:
Python 版本的实现相对简洁,因为 Python 列表可以动态扩容,无需显式管理内存。从列表末尾开始遍历,更新进位和元素值,如果最高位有进位,则在列表最前面插入 1。
Golang版本
如果 carry 仍然大于 0,这意味着原始数组的最高位也发生了进位,需要在数组的最前面插入一个数字 1。在 Golang 中,我们不能像 Python 那样简单地使用 insert
方法,因为 Go 的切片是固定长度的,我们需要创建一个新的切片来容纳这个额外的元素。
下面是 Golang 版本的完整实现:
func plusOne(digits []int) []int {
n := len(digits)
carry := 1
// 从后向前遍历数组,处理进位
for i := n - 1; i >= 0; i-- {
sum := digits[i] + carry
digits[i] = sum % 10
carry = sum / 10
}
// 如果最高位仍有进位,则需要在数组最前面插入一个 1
if carry > 0 {
// 创建一个新的切片,长度比原切片多 1
newDigits := make([]int, n+1)
// 将原切片的元素复制到新切片的相应位置
copy(newDigits[1:], digits)
// 在新切片的最前面插入进位值 1
newDigits[0] = carry
// 返回新切片
return newDigits
}
// 如果没有进位,直接返回原切片
return digits
}
在这个实现中,我们首先处理了数组中的进位,然后检查是否需要在数组最前面插入一个数字 1。如果需要,我们创建了一个新的切片 newDigits
,它的长度比原切片 digits
多 1,然后将原切片的元素复制到新切片的从索引 1 开始的位置,最后将进位值 1 插入到新切片的最前面。如果没有进位发生,我们则直接返回原切片。
请注意,这个实现假设输入的切片 digits
至少包含一个元素,因为题目要求是对一个非空数组进行操作。如果需要对空数组进行处理,那么还需要添加额外的边界检查。此外,由于 Go 语言的切片是引用类型,因此返回新的切片时,实际上返回的是对底层数组的引用,这不会造成额外的内存开销(除了可能的新切片本身)。