Masalah Subarray Maksimum di Jawa

1. Gambaran keseluruhan

Masalah subarray maksimum adalah tugas untuk mencari rangkaian elemen bersebelahan dengan jumlah maksimum dalam larik tertentu.

Sebagai contoh, dalam susunan di bawah ini, subarray yang disorot mempunyai jumlah maksimum (6):

Dalam tutorial ini, kita akan melihat dua penyelesaian untuk mencari subarray maksimum dalam array . Salah satu yang akan kami reka dengan kerumitan masa dan ruang O (n) .

2. Algoritma Brute Force

Brute force adalah pendekatan berulang untuk menyelesaikan masalah. Dalam kebanyakan kes, penyelesaiannya memerlukan sejumlah lelaran ke atas struktur data. Dalam beberapa bahagian seterusnya, kami akan menerapkan pendekatan ini untuk menyelesaikan masalah subarray maksimum.

2.1. Pendekatan

Secara umum, penyelesaian pertama yang terlintas di fikiran adalah mengira jumlah setiap subarray yang mungkin dan mengembalikan satu dengan jumlah maksimum.

Sebagai permulaan, kami akan mengira jumlah setiap subarray yang bermula pada indeks 0. Dan juga, kami akan menemui semua subarray bermula pada setiap indeks dari 0 hingga n-1 di mana n adalah panjang array:

Oleh itu, kita akan bermula pada indeks 0 dan menambahkan setiap elemen pada jumlah berjalan dalam lelaran. Kami juga akan memantau jumlah maksimum yang dilihat setakat ini . Iterasi ini ditunjukkan di sebelah kiri gambar di atas.

Di sebelah kanan gambar, kita dapat melihat lelaran yang bermula pada indeks 3 . Di bahagian terakhir gambar ini, kita mempunyai subarray dengan jumlah maksimum antara indeks 3 dan 6 .

Walau bagaimanapun, algoritma kami akan terus mencari semua subarrays bermula dari indeks antara 0 dan n-1 .

2.2. Pelaksanaan

Sekarang mari kita lihat bagaimana kita dapat melaksanakan penyelesaian ini di Java:

public int maxSubArray(int[] nums) { int n = nums.length; int maximumSubArraySum = Integer.MIN_VALUE; int start = 0; int end = 0; for (int left = 0; left < n; left++) { int runningWindowSum = 0; for (int right = left; right  maximumSubArraySum) { maximumSubArraySum = runningWindowSum; start = left; end = right; } } } logger.info("Found Maximum Subarray between {} and {}", start, end); return maximumSubArraySum; }

Seperti yang dijangkakan, kami mengemas kini maksimumSubArraySum jika jumlah semasa lebih banyak daripada jumlah maksimum sebelumnya. Terutama, kami juga mengemas kini permulaan dan akhir untuk mengetahui lokasi indeks subarray ini .

2.3. Kerumitan

Secara amnya, penyelesaian brute force berulang berulang kali untuk mendapatkan setiap penyelesaian yang mungkin. Ini bermaksud masa yang diambil oleh penyelesaian ini bertambah secara kuadratik dengan bilangan elemen dalam larik. Ini mungkin tidak menjadi masalah untuk tatasusunan bersaiz kecil. Tetapi apabila saiz array bertambah, penyelesaian ini tidak cekap.

Dengan memeriksa kod, kita juga dapat melihat bahawa terdapat dua bersarang untuk gelung. Oleh itu, kita dapat membuat kesimpulan bahawa kerumitan masa algoritma ini adalah O (n2) .

Pada bahagian seterusnya, kami akan menyelesaikan masalah ini dalam kerumitan O (n) menggunakan pengaturcaraan dinamik.

3. Pengaturcaraan Dinamik

Pengaturcaraan dinamik menyelesaikan masalah dengan membahagikannya kepada sub-masalah yang lebih kecil. Ini sangat serupa dengan teknik penyelesaian algoritma divide-and-win. Perbezaan utama, bagaimanapun, adalah bahawa pengaturcaraan dinamik menyelesaikan satu masalah hanya sekali.

Ia kemudian menyimpan hasil dari subproblem ini dan kemudian menggunakan kembali hasil ini untuk menyelesaikan sub-masalah lain yang berkaitan. Proses ini dikenali sebagai penghafalan .

3.1. Algoritma Kadane

Algoritma Kadane adalah penyelesaian popular untuk masalah subarray maksimum dan penyelesaian ini didasarkan pada pengaturcaraan dinamik.

Cabaran yang paling penting dalam menyelesaikan masalah pengaturcaraan dinamik adalah mencari sub-masalah yang optimum .

3.2. Pendekatan

Mari fahami masalah ini dengan cara yang berbeza:

Dalam gambar di atas, kami menganggap bahawa subarray maksimum berakhir di lokasi indeks terakhir. Oleh itu, jumlah maksimum subarray adalah:

maximumSubArraySum = max_so_far + arr[n-1]

max_so_far adalah jumlah maksimum subarray yang berakhir pada indeks n-2 . Ini juga ditunjukkan dalam gambar di atas.

Sekarang, kita dapat menerapkan andaian ini ke indeks apa pun dalam array. Sebagai contoh, jumlah subarray maksimum yang berakhir pada n-2 dapat dikira sebagai:

maximumSubArraySum[n-2] = max_so_far[n-3] + arr[n-2]

Oleh itu, kita dapat menyimpulkan bahawa:

maximumSubArraySum[i] = maximumSubArraySum[i-1] + arr[i]

Sekarang, kerana setiap elemen dalam array adalah subarray khas dengan ukuran satu, kita juga perlu memeriksa apakah elemen lebih besar daripada jumlah maksimum itu sendiri:

maximumSubArraySum[i] = Max(arr[i], maximumSubArraySum[i-1] + arr[i])

Dengan melihat persamaan ini, kita dapat melihat bahawa kita perlu mencari jumlah subarray maksimum pada setiap indeks array. Oleh itu, kami membahagikan masalah kami kepada n masalah. Kita dapat menjumpai jumlah maksimum pada setiap indeks dengan mengulang array hanya sekali:

Elemen yang diserlahkan menunjukkan elemen semasa dalam lelaran. Pada setiap indeks, kami akan menerapkan persamaan yang diperoleh lebih awal untuk mengira nilai untuk max_ending_here . Ini membantu kita dalam mengenal pasti sama ada kita harus memasukkan elemen semasa dalam subarray atau memulakan subarray baru bermula dari indeks ini .

Another variable, max_so_far is used to store the maximum subarray sum found during the iteration. Once we iterate over the last index, max_so_far will store the sum of the maximum subarray.

3.3. Implementation

Again, let's see how we can now implement the Kadane algorithm in Java, following the above approach:

public int maxSubArraySum(int[] arr) {       int size = arr.length;     int start = 0;     int end = 0;       int maxSoFar = 0, maxEndingHere = 0;       for (int i = 0; i  maxEndingHere + arr[i]) {             start = i;             maxEndingHere = arr[i];         } else             maxEndingHere = maxEndingHere + arr[i];           if (maxSoFar < maxEndingHere) {             maxSoFar = maxEndingHere;             end = i;         }     } logger.info("Found Maximum Subarray between {} and {}", start, end);     return maxSoFar; }

Here, we updated the start and end to find the maximum subarray indices.

3.4. Complexity

Since we only need to iterate the array once, the time complexity of this algorithm is O(n).

So as we can see, the time taken by this solution grows linearly with the number of elements in the array. Consequently, it is more efficient than the brute force approach we discussed in the last section.

4. Conclusion

Dalam tutorial ringkas ini, kami telah menerangkan dua cara untuk menyelesaikan masalah subarray maksimum.

Pertama, kami meneroka pendekatan brute force dan melihat bahawa penyelesaian berulang ini menghasilkan masa kuadratik. Kemudian, kami kemudian membincangkan algoritma Kadane dan menggunakan pengaturcaraan dinamik untuk menyelesaikan masalah dalam masa linear.

Seperti biasa, kod sumber penuh artikel terdapat di GitHub.