Masalah ini kelihatan mudah untuk diselesaikan dalam masa dan ruang linear. Masalah ini dibina berdasarkan beberapa konsep asas tatasusunan.
Syarikat yang telah bertanya perkara ini dalam temu bual pengekodan mereka ialah Facebook, Amazon, Apple, Netflix, Google, Microsoft, Adobe dan banyak lagi syarikat berteknologi tinggi.
Memandangkan nombor tatasusunan integer, kembalikan jawapan tatasusunan supaya jawapan[i] adalah sama dengan hasil darab semua elemen nombor kecuali nombor[i].
Produk mana-mana awalan atau akhiran nombor adalah dijamin untuk dimuatkan dalam 32-bit integer.
Anda mesti menulis algoritma yang berjalan dalam masa O(n) dan tanpa menggunakan operasi bahagi.
Kes ujian#1:
Input: nums = [1,2,3,4] Output: [24,12,8,6]
Kes ujian#2:
Input: nums = [-1,1,0,-3,3] Output: [0,0,9,0,0]
Masalah ini kelihatan lebih mudah untuk diselesaikan dalam masa dan ruang linear, tetapi ia adalah rumit apabila menulis pseudokod atau pelaksanaan kod sebenar.
Mari kita lihat hasil yang diharapkan daripada tatasusunan ringkas yang mengandungi 4 elemen:
input = {1, 2, 3, 4}
Jadi, nilai pada setiap indeks ialah hasil darab semua elemen lain dalam tatasusunan kecuali nilai itu sendiri. Rajah berikut menggambarkan ini.
Berdasarkan rajah di atas, kita boleh menghasilkan formula. Untuk sebarang indeks i tertentu, kita boleh mencari nilai menggunakan hasil darab unsur dari o hingga (i - 1) campur hasil darab unsur dari (i 1) hingga (N - 1). Ini digambarkan dalam rajah berikut:
Sebelum menulis kod pseudo, buat soalan dan tanya penemuduga.
Setelah anda dan penemuduga telah membincangkan soalan di atas, cipta pelbagai pendekatan untuk menyelesaikan masalah tersebut.
Untuk menggunakan pendekatan kekerasan, kita mesti melaksanakan dua gelung untuk. Apabila indeks gelung luar sepadan dengan nilai indeks gelung dalam, kita harus melangkau produk; jika tidak, kami meneruskan produk.
// 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; }
Satu cara yang difikirkan oleh kebanyakan pembangun ialah menjalankan jumlah produk bagi semua elemen, membahagikan jumlah produk dengan setiap nilai tatasusunan dan mengembalikan hasilnya.
// 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; }
Bagaimana jika salah satu elemen tatasusunan mengandungi 0? ?
Nilai pada semua indeks kecuali indeks yang mengandungi 0 pasti akan menjadi infiniti. Selain itu, kod tersebut membuang 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)
Ketahui lebih lanjut tentang jumlah awalan dan akhiran dalam Kursus Penguasaan Tatasusunan di tapak web saya https://ggorantala.dev
Awalan dan Akhiran dikira sebelum menulis algoritma untuk hasilnya. Formula jumlah awalan dan akhiran diberikan di bawah:
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.
Atas ialah kandungan terperinci Leetcode : Produk Tatasusunan Kecuali Diri. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!