Description
給定整數陣列
nums,回傳陣列answer,其中answer[i]等於nums中除了nums[i]以外所有元素的乘積。不能使用除法,且必須在 O(n) 時間內完成。
Example:
Input: nums = [1,2,3,4] Output: [24,12,8,6]
Intuition#
核心思路:
answer[i] = 左邊所有數的乘積 * 右邊所有數的乘積,用前綴積和後綴積拆解。
- 不能用除法,所以不能「全部乘起來再除以 nums[i]」
- 每個位置的答案可以拆成:左側前綴積 x 右側後綴積
- 可以先用一個陣列存前綴積,再反向掃一遍乘上後綴積,達到 O(1) 額外空間
Approaches#
1: Two Arrays (Prefix + Suffix)#
- 概念: 分別建立前綴積陣列和後綴積陣列,
answer[i] = prefix[i] * suffix[i] - 時間複雜度:
O(n)- 三次線性遍歷 - 空間複雜度:
O(n)- 額外的前綴和後綴陣列
class Solution {
fun productExceptSelf(nums: IntArray): IntArray {
val n = nums.size
val prefix = IntArray(n) { 1 }
val suffix = IntArray(n) { 1 }
for (i in 1 until n) {
prefix[i] = prefix[i - 1] * nums[i - 1]
}
for (i in n - 2 downTo 0) {
suffix[i] = suffix[i + 1] * nums[i + 1]
}
return IntArray(n) { prefix[it] * suffix[it] }
}
}⭐2: Single Array with Running Suffix#
- 概念: 先將前綴積直接存入結果陣列,再用一個變數從右往左累積後綴積,逐一乘入結果
- 時間複雜度:
O(n)- 兩次線性遍歷 - 空間複雜度:
O(1)- 除了輸出陣列外只用一個變數(題目聲明輸出陣列不計入空間)
class Solution {
fun productExceptSelf(nums: IntArray): IntArray {
val n = nums.size
val answer = IntArray(n)
// 前綴積存入 answer
answer[0] = 1
for (i in 1 until n) {
answer[i] = answer[i - 1] * nums[i - 1]
}
// 反向掃描,用 suffix 變數累積後綴積
var suffix = 1
for (i in n - 1 downTo 0) {
answer[i] *= suffix
suffix *= nums[i]
}
return answer
}
}題目明確說「output array does not count as extra space」,所以第二種解法的空間複雜度被視為 O(1)。面試時記得確認這個假設。
🔑 Takeaways#
- Pattern: Prefix / Suffix 分解——將每個位置的答案拆成「左邊貢獻」和「右邊貢獻」
- 關鍵技巧: 先把一個方向的結果存入答案陣列,再用一個變數從反方向掃描累積,省去第二個陣列。此技巧可推廣到 Trapping Rain Water 等題目