LeetCode 解題思路:66. Plus One(加一)
陣列模擬加法運算與進位處理技巧
目錄
題目描述
給定一個由整數陣列表示的大整數 digits
,其中每個 digits[i]
是該整數的第 i
位數字。數字按照從左到右的順序排列,從最高位到最低位。該大整數不包含任何前導零。
將該大整數加一,並返回結果的數字陣列。
範例
範例 1:
輸入:digits = [1,2,3]
輸出:[1,2,4]
解釋:該陣列表示整數 123。
123 + 1 = 124
範例 2:
輸入:digits = [4,3,2,1]
輸出:[4,3,2,2]
解釋:該陣列表示整數 4321。
4321 + 1 = 4322
範例 3:
輸入:digits = [9]
輸出:[1,0]
解釋:該陣列表示整數 9。
9 + 1 = 10
範例 4:
輸入:digits = [9,9,9]
輸出:[1,0,0,0]
解釋:999 + 1 = 1000
限制條件
1 <= digits.length <= 100
0 <= digits[i] <= 9
digits
不包含任何前導零
核心概念理解
1. 問題本質
這題模擬的是我們手算加法的過程:
- 從最低位(最右邊)開始加 1
- 處理進位(carry)
- 如果一直進位到最高位,需要擴展陣列
2. 進位的觸發條件
當某一位 = 9 時,加 1 會產生進位:
9 + 1 = 10 → 該位變成 0,進位 1
例如:
[1, 2, 9] + 1 = [1, 3, 0]
[9, 9, 9] + 1 = [1, 0, 0, 0]
3. 特殊情況分析
情況 1:末位不是 9
[1, 2, 3] + 1 = [1, 2, 4] # 直接加 1,無進位
情況 2:末尾有連續的 9
[1, 2, 9, 9] + 1 = [1, 3, 0, 0] # 連續進位
情況 3:全部都是 9
[9, 9, 9] + 1 = [1, 0, 0, 0] # 需要擴展陣列
解法一:從後往前遍歷(推薦)
思路
模擬手算加法的過程,從最低位開始處理進位。
程式碼實現
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
n = len(digits)
# 從最低位(最右邊)開始
for i in range(n - 1, -1, -1):
# 當前位加 1
digits[i] += 1
# 如果小於 10,沒有進位,直接返回
if digits[i] < 10:
return digits
# 有進位,當前位設為 0,繼續處理下一位
digits[i] = 0
# 如果循環結束還沒返回,說明最高位也有進位
# 例如 [9,9,9] 變成 [0,0,0],需要在前面加 1
return [1] + digits
執行過程視覺化
範例 1:digits = [1,2,3]
[1, 2, 3]
↑
加1=4 (< 10,無進位)
結果:[1, 2, 4]
範例 2:digits = [1,2,9]
[1, 2, 9]
↑
加1=10 (進位)
[1, 2, 0]
↑
加1=3 (< 10,停止)
結果:[1, 3, 0]
範例 3:digits = [9,9,9]
[9, 9, 9]
↑
加1=10 (進位)
[9, 9, 0]
↑
加1=10 (進位)
[9, 0, 0]
↑
加1=10 (進位)
[0, 0, 0]
需要在前面加 1
結果:[1, 0, 0, 0]
複雜度分析
- 時間複雜度:O(n),最壞情況需要遍歷所有位數
- 空間複雜度:O(1),如果不考慮返回值;O(n) 如果考慮可能需要擴展陣列
解法二:使用進位變數
思路
使用一個 carry 變數來追蹤進位狀態。
程式碼實現
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
carry = 1 # 初始要加的值
n = len(digits)
# 從後往前處理
for i in range(n - 1, -1, -1):
total = digits[i] + carry
digits[i] = total % 10
carry = total // 10
# 如果沒有進位了,可以提前結束
if carry == 0:
return digits
# 如果還有進位,說明需要在前面加 1
if carry:
return [carry] + digits
return digits
變數追蹤
digits = [9, 9, 9], carry = 1
i = 2: total = 9 + 1 = 10
digits[2] = 0, carry = 1
i = 1: total = 9 + 1 = 10
digits[1] = 0, carry = 1
i = 0: total = 9 + 1 = 10
digits[0] = 0, carry = 1
carry = 1,需要在前面加 1
結果:[1, 0, 0, 0]
解法三:優化版(提前終止)
思路
觀察發現:只有當某位是 9 時才會產生進位,否則加 1 後就可以直接返回。
程式碼實現
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
n = len(digits)
# 從後往前找第一個不是 9 的位置
for i in range(n - 1, -1, -1):
if digits[i] != 9:
digits[i] += 1
return digits
# 是 9,設為 0,繼續往前
digits[i] = 0
# 所有位都是 9
return [1] + [0] * n
優化說明
# 更簡潔的寫法
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
for i in range(len(digits) - 1, -1, -1):
if digits[i] < 9:
digits[i] += 1
return digits
digits[i] = 0
return [1] + digits
解法四:遞迴解法
思路
將問題分解為子問題:如果當前位產生進位,就對前面的部分執行加一操作。
程式碼實現
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
def addOne(digits, index):
# 基礎情況:已經處理到最前面
if index < 0:
return [1] + digits
# 當前位加 1
digits[index] += 1
# 如果產生進位
if digits[index] == 10:
digits[index] = 0
return addOne(digits, index - 1)
return digits
return addOne(digits, len(digits) - 1)
解法五:數學轉換(不推薦用於大數)
思路
將陣列轉換成整數,加一後再轉回陣列。
程式碼實現
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
# 轉換為整數
num = 0
for digit in digits:
num = num * 10 + digit
# 加一
num += 1
# 轉回陣列
return [int(d) for d in str(num)]
為什麼不推薦?
# 問題:整數溢出
# 題目說 digits.length 可達 100
# 這表示數字可能有 100 位!
# 大多數程式語言的整數類型無法處理這麼大的數
# Python 可以處理任意大的整數,但:
# 1. 效率較低
# 2. 失去了練習陣列操作的意義
# 3. 面試時可能被要求不使用這種方法
常見錯誤與陷阱
錯誤 1:忘記處理全 9 的情況
# ❌ 錯誤:沒有考慮需要擴展陣列
def plusOne(self, digits: List[int]) -> List[int]:
for i in range(len(digits) - 1, -1, -1):
if digits[i] < 9:
digits[i] += 1
return digits
digits[i] = 0
# 忘記處理 [9,9,9] → [1,0,0,0]
錯誤 2:修改了原陣列但沒有正確返回
# ❌ 錯誤:創建新陣列但邏輯錯誤
def plusOne(self, digits: List[int]) -> List[int]:
result = digits[:] # 複製陣列
carry = 1
for i in range(len(result) - 1, -1, -1):
result[i] += carry
if result[i] < 10:
break
result[i] = 0
# 忘記處理最高位進位
return result
錯誤 3:邊界索引錯誤
# ❌ 錯誤:索引計算錯誤
def plusOne(self, digits: List[int]) -> List[int]:
n = len(digits)
for i in range(n, 0, -1): # 應該是 range(n-1, -1, -1)
digits[i] += 1 # IndexError!
變體問題
1. Plus K(加任意數字)
def plusK(digits: List[int], k: int) -> List[int]:
carry = k
n = len(digits)
result = []
# 從後往前處理
for i in range(n - 1, -1, -1):
total = digits[i] + carry
result.append(total % 10)
carry = total // 10
# 處理剩餘的進位
while carry:
result.append(carry % 10)
carry //= 10
# 反轉結果
return result[::-1]
# 測試
print(plusK([9, 9, 9], 25)) # [1, 0, 2, 4]
2. 減一操作
def minusOne(digits: List[int]) -> List[int]:
n = len(digits)
for i in range(n - 1, -1, -1):
if digits[i] > 0:
digits[i] -= 1
# 移除前導零
if digits[0] == 0 and n > 1:
return digits[1:]
return digits
# 需要借位
digits[i] = 9
# 不應該到這裡(假設輸入 > 0)
return digits
3. 二進制加一
def addBinary(binary: List[int]) -> List[int]:
"""二進制陣列加一"""
n = len(binary)
for i in range(n - 1, -1, -1):
if binary[i] == 0:
binary[i] = 1
return binary
binary[i] = 0
return [1] + binary
# 測試
print(addBinary([1, 0, 1])) # [1, 1, 0]
print(addBinary([1, 1, 1])) # [1, 0, 0, 0]
4. 加法器(兩個陣列相加)
def addTwoNumbers(num1: List[int], num2: List[int]) -> List[int]:
# 確保 num1 是較長的
if len(num1) < len(num2):
num1, num2 = num2, num1
n1, n2 = len(num1), len(num2)
carry = 0
result = []
# 從後往前處理
for i in range(n1):
# 計算對應位置的和
j = n2 - (n1 - i)
digit2 = num2[j] if j >= 0 else 0
total = num1[i] + digit2 + carry
result.append(total % 10)
carry = total // 10
if carry:
result.append(carry)
return result[::-1]
效能優化
1. 原地修改 vs 創建新陣列
# 原地修改(空間 O(1))
def plusOne_inplace(digits: List[int]) -> List[int]:
for i in range(len(digits) - 1, -1, -1):
if digits[i] < 9:
digits[i] += 1
return digits
digits[i] = 0
return [1] + digits
# 創建新陣列(空間 O(n))
def plusOne_new_array(digits: List[int]) -> List[int]:
result = digits[:]
# ... 處理邏輯
return result
2. 提前終止的重要性
import time
def benchmark_solutions():
# 最壞情況:全是 9
worst_case = [9] * 100
# 最好情況:末位不是 9
best_case = [9] * 99 + [8]
# 測試提前終止的效果
iterations = 100000
# 最壞情況
start = time.time()
for _ in range(iterations):
plusOne(worst_case[:])
print(f"最壞情況: {time.time() - start:.4f}s")
# 最好情況
start = time.time()
for _ in range(iterations):
plusOne(best_case[:])
print(f"最好情況: {time.time() - start:.4f}s")
進階思考
1. 處理負數
def increment(digits: List[int], sign: int = 1) -> Tuple[List[int], int]:
"""
處理帶符號的數字
返回 (digits, sign)
"""
if sign == 1:
# 正數,正常加一
return plusOne(digits), 1
else:
# 負數,實際上是減少絕對值
return minusOne(digits), -1
2. 任意進制
def plusOne_base(digits: List[int], base: int) -> List[int]:
"""任意進制的加一操作"""
n = len(digits)
for i in range(n - 1, -1, -1):
digits[i] += 1
if digits[i] < base:
return digits
digits[i] = 0
return [1] + digits
# 八進制例子
print(plusOne_base([7, 7, 7], 8)) # [1, 0, 0, 0]
3. 大數運算庫的實現思路
class BigInteger:
def __init__(self, digits):
self.digits = digits
self.base = 10
def add(self, other):
"""加法運算"""
# 實現細節...
pass
def multiply(self, other):
"""乘法運算"""
# 實現細節...
pass
測試案例
def test_plusOne():
test_cases = [
# (input, expected)
([1, 2, 3], [1, 2, 4]),
([4, 3, 2, 1], [4, 3, 2, 2]),
([9], [1, 0]),
([9, 9, 9], [1, 0, 0, 0]),
([0], [1]),
([1, 9, 9], [2, 0, 0]),
([8, 9, 9, 9], [9, 0, 0, 0]),
([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [9, 8, 7, 6, 5, 4, 3, 2, 1, 1]),
]
solution = Solution()
for digits, expected in test_cases:
result = solution.plusOne(digits[:]) # 複製以避免修改原陣列
assert result == expected, f"Failed: {digits}"
print(f"✅ 通過:{digits} + 1 = {result}")
相關題目
- LeetCode 67: Add Binary(二進制加法)
- LeetCode 369: Plus One Linked List(鏈表加一)
- LeetCode 989: Add to Array-Form of Integer(陣列形式的整數加法)
- LeetCode 2: Add Two Numbers(兩數相加)
- LeetCode 445: Add Two Numbers II(兩數相加 II)
面試技巧
1. 問題澄清
面試官:"實現陣列表示的整數加一。"
你應該問:
1. "陣列是否可能為空?"
2. "是否需要處理負數?"
3. "數字的範圍有多大?會不會超過語言的整數限制?"
4. "可以修改原陣列嗎?"
5. "有前導零嗎?"
2. 解題思路展示
"""
思路分析:
1. 這是一個進位問題,類似手算加法
2. 從最低位開始處理
3. 關鍵是處理連續的 9
4. 特殊情況:全是 9 時需要擴展陣列
時間複雜度:O(n)
空間複雜度:O(1) 或 O(n)(如果需要擴展)
"""
3. 優化討論
- 提前終止的重要性
- 空間複雜度的權衡
- 是否需要支援更通用的加法
總結
這道題雖然標記為 Easy,但包含了幾個重要概念:
- 進位處理:理解數學運算的本質
- 邊界情況:特別是全 9 的情況
- 陣列操作:從後往前遍歷的技巧
- 優化思維:提前終止可以提升效能
核心要點:
- 從低位到高位處理
- 遇到不是 9 的位就可以停止
- 記得處理最高位進位的特殊情況
這道題是理解大數運算的基礎,掌握後可以擴展到更複雜的運算!