Leetcode:除自身之外的陣列的乘積
這個問題在線性時間和空間中看起來很容易解決。這個問題建立在數組的一些基本概念之上。
- 數組遍歷。
- 前綴和後綴之和。
在程式設計面試中提出這個問題的公司有 Facebook、亞馬遜、蘋果、Netflix、Google、微軟、Adobe 以及許多其他頂尖科技公司。
問題陳述
給定一個整數數組 nums,傳回一個數組 answer,使得 answer[i] 等於 nums 中除 nums[i] 之外的所有元素的乘積。
nums 的任何前綴或後綴的乘積保證適合32位元整數。
您必須編寫一個在 O(n) 時間內運作且不使用除法運算的演算法。
測試案例#1:
Input: nums = [1,2,3,4] Output: [24,12,8,6]
測試案例#2:
Input: nums = [-1,1,0,-3,3] Output: [0,0,9,0,0]
了解問題
這個問題在線性時間和空間上看起來更容易解決,但在編寫偽代碼或實際程式碼實作時卻很棘手。
插圖
讓我們來看看包含 4 個元素的簡單陣列的預期結果:
input = {1, 2, 3, 4}
因此,每個索引處的值是數組中除該值本身之外的所有其他元素的乘積。下圖說明了這一點。
根據上圖,我們可以得到一個公式。對於任何給定的索引 i,我們可以使用從 o 到 (i - 1) 的元素的乘積加上從 (i 1) 到 (N - 1) 的元素的乘積來找到該值。如下圖:
思維過程
在寫偽代碼之前,先提出問題並向面試官提問。
- 我該擔心重複嗎?
- 如果陣列為空或只有一個元素怎麼辦?預期結果是什麼?
- 我應該考慮/忽略數組中任何索引中的 0 值嗎?因為除了包含 0 的索引之外,所有其他值都為 0。
- 這個問題的極端/邊緣情況是什麼?
一旦你和麵試官討論了上述問題,就想出各種方法來解決問題。
- 天真的方法/暴力。
- 所有元素的乘積。
- 左右產品。
- 前綴和後綴總和。
方法 1:樸素/暴力
直覺
要採用強力方法,我們必須執行兩個 for 迴圈。當外循環索引與內循環索引值相符時,我們應該跳過乘積;否則,我們將繼續使用該產品。
演算法
- 初始化變數:
- N = nums.length(輸入陣列的長度)。
- result = new int[N] (儲存結果的陣列)。
- 外循環(迭代 nums 中的每個元素):
- 對於 i = 0 到 N-1:初始化 currentProduct = 1。
- 內循環(計算當前元素的乘積),對於 j = 0 到 N-1:
- 如果 i == j,則使用 continue 跳過目前迭代。
- 將 currentProduct 乘以 nums[j]。
- 將 currentProduct 分配給 result[i]。
- 傳回結果。
程式碼
// brute force static int[] bruteForce(int[] nums) { int N = nums.length; int[] result = new int[N]; for (int i = 0; i < N; i++) { int currentProduct = 1; for (int j = 0; j < N; j++) { if (i == j) { continue; } currentProduct *= nums[j]; } result[i] = currentProduct; } return result; }
複雜度分析
- 時間複雜度:O(n^2),用於在外循環和內循環中迭代數組兩次。
- 空間複雜度:O(n),對於我們使用的輔助空間(result[]陣列)。
方法 2:陣列 ❌ 的乘積
大多數開發人員認為的一種方法是運行所有元素的乘積和,將乘積和除以每個數組值,然後返回結果。
虛擬程式碼
// O(n) time and O(1) space p = 1 for i -> 0 to A[i] p * = A[i] for i -> 0 to (N - 1) A[i] = p/A[i] // if A[i] == 0 ? BAM error‼️
程式碼
// code implementation static int[] productSum(int[] nums) { int product_sum = 1; for(int num: nums) { product_sum *= num; } for(int i = 0; i < nums.length; i++) { nums[i] = product_sum/nums[i]; } return nums; }
如果陣列元素之一包含 0 怎麼辦? ?
除了包含0的索引之外,所有索引的值肯定是無限大。另外,程式碼會拋出 java.lang.ArithmeticException。
Exception in thread "main" java.lang.ArithmeticException: / by zero at dev.ggorantala.ds.arrays.ProductOfArrayItself.productSum(ProductOfArrayItself.java:24) at dev.ggorantala.ds.arrays.ProductOfArrayItself.main(ProductOfArrayItself.java:14)
方法 3:找出前綴和後綴乘積
在我的網站上的數組掌握課程中了解有關前綴和後綴和的更多信息 https://ggorantala.dev
直覺與公式
前綴和後綴是在為結果編寫演算法之前計算的。字首和字尾總和公式如下:
Algorithm Steps
- Create an array result of the same length as nums to store the final results.
- Create two additional arrays prefix_sum and suffix_sum of the same length as nums.
- Calculate Prefix Products:
- Set the first element of prefix_sum to the first element of nums.
- Iterate through the input array nums starting from the second element (index 1). For each index i, set prefix_sum[i] to the product of prefix_sum[i-1] and nums[i].
- Calculate Suffix Products:
- Set the last element of suffix_sum to the last element of nums.
- Iterate through the input array nums starting from the second-to-last element (index nums.length - 2) to the first element. For each index i, set suffix_sum[i] to the product of suffix_sum[i+1] and nums[i].
- Calculate the result: Iterate through the input array nums.
- For the first element (i == 0), set result[i] to suffix_sum[i + 1].
- For the last element (i == nums.length - 1), set result[i] to prefix_sum[i - 1].
- For all other elements, set result[i] to the product of prefix_sum[i - 1] and suffix_sum[i + 1].
- Return the result array containing the product of all elements except the current element for each index.
Pseudocode
Function usingPrefixSuffix(nums): N = length of nums result = new array of length N prefix_sum = new array of length N suffix_sum = new array of length N // Calculate prefix products prefix_sum[0] = nums[0] For i from 1 to N-1: prefix_sum[i] = prefix_sum[i-1] * nums[i] // Calculate suffix products suffix_sum[N-1] = nums[N-1] For i from N-2 to 0: suffix_sum[i] = suffix_sum[i+1] * nums[i] // Calculate result array For i from 0 to N-1: If i == 0: result[i] = suffix_sum[i+1] Else If i == N-1: result[i] = prefix_sum[i-1] Else: result[i] = prefix_sum[i-1] * suffix_sum[i+1] Return result
Code
// using prefix and suffix arrays private static int[] usingPrefixSuffix(int[] nums) { int[] result = new int[nums.length]; int[] prefix_sum = new int[nums.length]; int[] suffix_sum = new int[nums.length]; // prefix sum calculation prefix_sum[0] = nums[0]; for (int i = 1; i < nums.length; i++) { prefix_sum[i] = prefix_sum[i - 1] * nums[i]; } // suffix sum calculation suffix_sum[nums.length - 1] = nums[nums.length - 1]; for (int i = nums.length - 2; i >= 0; i--) { suffix_sum[i] = suffix_sum[i + 1] * nums[i]; } for (int i = 0; i < nums.length; i++) { if (i == 0) { // when variable `i` is at 0th index result[i] = suffix_sum[i + 1]; } else if (i == nums.length - 1) { // when variable `i` is at last index result[i] = prefix_sum[i - 1]; } else { // for all other indexes result[i] = prefix_sum[i - 1] * suffix_sum[i + 1]; } } return result; }
Complexity analysis
-
Time complexity: The time complexity of the given code is O(n), where n is the length of the input array nums. This is because:
- Calculating the prefix_sum products take O(n) time.
- Calculating the suffix_sum products take O(n) time.
- Constructing the result array takes O(n) time.
Each of these steps involves a single pass through the array, resulting in a total time complexity of O(n)+O(n)+O(n) = 3O(n), which is O(n).
-
Space complexity: The space complexity of the given code is O(n). This is because:
- The prefix_sum array requires O(n) space.
- The suffix_sum array requires O(n) space.
- Theresult array requires O(n) space. All three arrays are of length n, so the total space complexity is O(n) + O(n) + O(n) = 3O(n), which is O(n).
Optimization ?
For the suffix array calculation, we override the input nums array instead of creating one.
private static int[] usingPrefixSuffixOptimization(int[] nums) { int[] result = new int[nums.length]; int[] prefix_sum = new int[nums.length]; // prefix sum calculation prefix_sum[0] = nums[0]; for (int i = 1; i < nums.length; i++) { prefix_sum[i] = prefix_sum[i - 1] * nums[i]; } // suffix sum calculation, in-place - `nums` array override for (int i = nums.length - 2; i >= 0; i--) { nums[i] = nums[i + 1] * nums[i]; } for (int i = 0; i < nums.length; i++) { if (i == 0) { // when variable `i` is at 0th index result[i] = nums[i + 1]; } else if (i == nums.length - 1) { // when variable `i` is at last index result[i] = prefix_sum[i - 1]; } else { // for all other indexes result[i] = prefix_sum[i - 1] * nums[i + 1]; } } return result; }
Hence, we reduced the space of O(n). Time and space are not reduced, but we did a small optimization here.
Approach 4: Using Prefix and Suffix product knowledge ?
Intuition
This is a rather easy approach when we use the knowledge of prefix and suffix arrays.
For every given index i, we will calculate the product of all the numbers to the left and then multiply it by the product of all the numbers to the right. This will give us the product of all the numbers except the one at the given index i. Let's look at a formal algorithm that describes this idea more clearly.
Algorithm steps
- Create an array result of the same length as nums to store the final results.
- Create two additional arrays prefix_sum and suffix_sum of the same length as nums.
- Calculate Prefix Products:
- Set the first element of prefix_sum to 1.
- Iterate through the input array nums starting from the second element (index 1). For each index i, set prefix_sum[i] to the product of prefix_sum[i - 1] and nums[i - 1].
- Calculate Suffix Products:
- Set the last element of suffix_sum to 1.
- Iterate through the input array nums starting from the second-to-last element (index nums.length - 2) to the first element.
- For each index i, set suffix_sum[i] to the product of suffix_sum[i + 1] and nums[i + 1].
- Iterate through the input array nums.
- For each index i, set result[i] to the product of prefix_sum[i] and suffix_sum[i].
- Return the result array containing the product of all elements except the current element for each index.
Pseudocode
Function prefixSuffix1(nums): N = length of nums result = new array of length N prefix_sum = new array of length N suffix_sum = new array of length N // Calculate prefix products prefix_sum[0] = 1 For i from 1 to N-1: prefix_sum[i] = prefix_sum[i-1] * nums[i-1] // Calculate suffix products suffix_sum[N-1] = 1 For i from N-2 to 0: suffix_sum[i] = suffix_sum[i+1] * nums[i+1] // Calculate result array For i from 0 to N-1: result[i] = prefix_sum[i] * suffix_sum[i] Return result
Code
private static int[] prefixSuffixProducts(int[] nums) { int[] result = new int[nums.length]; int[] prefix_sum = new int[nums.length]; int[] suffix_sum = new int[nums.length]; prefix_sum[0] = 1; for (int i = 1; i < nums.length; i++) { prefix_sum[i] = prefix_sum[i - 1] * nums[i - 1]; } suffix_sum[nums.length - 1] = 1; for (int i = nums.length - 2; i >= 0; i--) { suffix_sum[i] = suffix_sum[i + 1] * nums[i + 1]; } for (int i = 0; i < nums.length; i++) { result[i] = prefix_sum[i] * suffix_sum[i]; } return result; }
Complexity analysis
-
Time complexity: The time complexity of the given code is O(n), where n is the length of the input array nums. This is because:
- Calculating the prefix_sum products take O(n) time.
- Calculating the suffix_sum products take O(n) time.
- Constructing the result array takes O(n) time.
Each of these steps involves a single pass through the array, resulting in a total time complexity of O(n)+O(n)+O(n) = 3O(n), which is O(n).
-
Space complexity: The space complexity of the given code is O(n). This is because:
- The prefix_sum array requires O(n) space.
- The suffix_sum array requires O(n) space.
- The result array requires O(n) space.
All three arrays are of length n, so the total space complexity is O(n) + O(n) + O(n) = 3O(n), which is O(n).
Approach 5: Carry Forward technique
Intuition
The carry forward technique optimizes us to solve the problem with a more efficient space complexity. Instead of using two separate arrays for prefix and suffix products, we can use the result array itself to store intermediate results and use a single pass for each direction.
Here’s how you can implement the solution using the carry-forward technique:
Algorithm Steps for Carry Forward Technique
- Initialize Result Array:
- Create an array result of the same length as nums to store the final results.
- Calculate Prefix Products:
- Initialize a variable prefixProduct to 1.
- Iterate through the input array nums from left to right. For each index i, set result[i] to the value of prefixProduct. Update prefixProduct by multiplying it with nums[i].
- Calculate Suffix Products and Final Result:
- Initialize a variable suffixProduct to 1.
- Iterate through the input array nums from right to left. For each index i, update result[i] by multiplying it with suffixProduct. Update suffixProduct by multiplying it with nums[i].
- Return the result array containing the product of all elements except the current element for each index.
Pseudocode
prefix products prefixProduct = 1 For i from 0 to N-1: result[i] = prefixProduct prefixProduct = prefixProduct * nums[i] // Calculate suffix products and finalize result suffixProduct = 1 For i from N-1 to 0: result[i] = result[i] * suffixProduct suffixProduct = suffixProduct * nums[i] Return result
Code
// carry forward technique private static int[] carryForward(int[] nums) { int n = nums.length; int[] result = new int[n]; // Calculate prefix products int prefixProduct = 1; for (int i = 0; i < n; i++) { result[i] = prefixProduct; prefixProduct *= nums[i]; } // Calculate suffix products and finalize the result int suffixProduct = 1; for (int i = n - 1; i >= 0; i--) { result[i] *= suffixProduct; suffixProduct *= nums[i]; } return result; }
Explanation
- Prefix Products Calculation:
- We initialize prefixProduct to 1 and update each element of result with the current value of prefixProduct.
- Update prefixProduct by multiplying it with nums[i].
- Suffix Products Calculation:
- We initialize suffixProduct to 1 and update each element of result(which already contains the prefix product) by multiplying it with suffixProduct.
- Update suffixProduct by multiplying it with nums[i].
Complexity analysis
- Time Complexity: O(n) time
- Space Complexity: O(n) (for the result array)
This approach uses only a single extra array (result) and two variables (prefixProduct and suffixProduct), achieving efficient space utilization while maintaining O(n) time complexity.
以上是Leetcode:除自身之外的陣列的乘積的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

將姓名轉換為數字以實現排序的解決方案在許多應用場景中,用戶可能需要在群組中進行排序,尤其是在一個用...

在使用IntelliJIDEAUltimate版本啟動Spring...

在使用MyBatis-Plus或其他ORM框架進行數據庫操作時,經常需要根據實體類的屬性名構造查詢條件。如果每次都手動...

系統對接中的字段映射處理在進行系統對接時,常常會遇到一個棘手的問題:如何將A系統的接口字段有效地映�...

Java對象與數組的轉換:深入探討強制類型轉換的風險與正確方法很多Java初學者會遇到將一個對象轉換成數組的�...

電商平台SKU和SPU表設計詳解本文將探討電商平台中SKU和SPU的數據庫設計問題,特別是如何處理用戶自定義銷售屬...

Redis緩存方案如何實現產品排行榜列表的需求?在開發過程中,我們常常需要處理排行榜的需求,例如展示一個�...
