Ce problème semble simple à résoudre dans le temps et dans l’espace linéaires. Ce problème s'appuie sur certains des concepts fondamentaux des tableaux.
Les entreprises qui ont posé cette question lors de leur entretien de codage sont Facebook, Amazon, Apple, Netflix, Google, Microsoft, Adobe et bien d'autres entreprises technologiques de premier plan.
Étant donné un tableau entier nums, renvoie un tableau réponse tel que réponse[i] est égal au produit de tous les éléments de nums sauf nums[i].
Le produit de tout préfixe ou suffixe de nombres est garanti pour tenir dans un entier 32 bits.
Vous devez écrire un algorithme qui s'exécute en temps O(n) et sans utiliser l'opération de division.
Cas de test n°1 :
Input: nums = [1,2,3,4] Output: [24,12,8,6]
Cas de test n°2 :
Input: nums = [-1,1,0,-3,3] Output: [0,0,9,0,0]
Ce problème semble plus simple à résoudre dans un temps et un espace linéaires, mais il est délicat lors de l'écriture du pseudocode ou de l'implémentation réelle du code.
Voyons quels sont les résultats attendus d'un simple tableau contenant 4 éléments :
input = {1, 2, 3, 4}
Ainsi, la valeur de chaque index est le produit de tous les autres éléments du tableau à l'exception de la valeur elle-même. La figure suivante illustre cela.
Sur la base du chiffre ci-dessus, nous pouvons trouver une formule. Pour tout indice i donné, nous pouvons trouver la valeur en utilisant le produit des éléments de o à (i - 1) plus le produit des éléments de (i 1) à (N - 1). Ceci est illustré dans la figure suivante :
Avant d'écrire du pseudo-code, posez des questions et posez-les à l'intervieweur.
Une fois que vous et l'intervieweur avez discuté des questions ci-dessus, concevez différentes approches pour résoudre le problème.
Pour utiliser l'approche par force brute, nous devons exécuter deux boucles for. Lorsque l'index de la boucle externe correspond à la valeur de l'index de la boucle interne, nous devons ignorer le produit ; sinon, nous procédons au produit.
// 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; }
La plupart des développeurs pensent qu'il faut exécuter une somme de produits de tous les éléments, diviser la somme de produits par chaque valeur du tableau et renvoyer le résultat.
// 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; }
Et si l'un des éléments du tableau contient 0 ? ?
La valeur de tous les index, à l'exception de l'index contenant 0, sera définitivement l'infini. De plus, le code renvoie 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)
Apprenez-en plus sur la somme des préfixes et des suffixes dans le cours de maîtrise des tableaux sur mon site Web https://ggorantala.dev
Le préfixe et le suffixe sont calculés avant d'écrire un algorithme pour le résultat. Les formules de somme des préfixes et des suffixes sont données ci-dessous :
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
// 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; }
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).
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.
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.
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
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; }
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).
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).
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:
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
// 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; }
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.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!