Apakah ada algoritma yang efisien selain pencarian brute force untuk menemukan tiga bilangan bulat?
Ya; kita bisa menyelesaikan ini dalam waktu O (n 2 )! Pertama, pertimbangkan bahwa masalah Anda Pdapat diungkapkan dengan cara yang sedikit berbeda sehingga menghilangkan kebutuhan akan "nilai target":
masalah asli P: Mengingat sebuah array Adari ninteger dan nilai target S, apakah terdapat 3-tupel dari Ajumlah itu untuk S?
dimodifikasi masalah P': Mengingat sebuah array Adari nbilangan bulat, tidak terdapat 3-tupel dari Ajumlah yang ke nol?
Perhatikan bahwa Anda dapat pergi dari versi ini masalah P'dari Pdengan mengurangkan S Anda / 3 dari setiap elemen dalam A, tapi sekarang Anda tidak perlu nilai target lagi.
Jelas, jika kita hanya menguji semua 3-tupel yang mungkin, kita akan menyelesaikan masalah di O (n 3 ) - itulah garis dasar brute-force. Apakah mungkin berbuat lebih baik? Bagaimana jika kita memilih tuple dengan cara yang agak lebih pintar?
Pertama, kami menginvestasikan waktu untuk mengurutkan array, yang dikenakan biaya penalti awal O (n log n). Sekarang kita jalankan algoritma ini:
for (i in 1..n-2) {
j = i+1 // Start right after i.
k = n // Start at the end of the array.
while (k >= j) {
// We got a match! All done.
if (A[i] + A[j] + A[k] == 0) return (A[i], A[j], A[k])
// We didn't match. Let's try to get a little closer:
// If the sum was too big, decrement k.
// If the sum was too small, increment j.
(A[i] + A[j] + A[k] > 0) ? k-- : j++
}
// When the while-loop finishes, j and k have passed each other and there's
// no more useful combinations that we can try with this i.
}
Algoritma ini bekerja dengan menempatkan tiga pointer, i, j, dan kpada berbagai titik dalam array. idimulai dari awal dan perlahan-lahan bekerja sampai akhir. kmenunjuk ke elemen terakhir. jmenunjuk ke tempat idimulainya. Kami berulang mencoba untuk menjumlahkan elemen pada indeks masing-masing, dan setiap kali salah satu dari berikut terjadi:
- Jumlahnya tepat! Kami telah menemukan jawabannya.
- Jumlahnya terlalu kecil. Bergerak
jlebih dekat ke ujung untuk memilih nomor terbesar berikutnya.
- Jumlahnya terlalu besar. Bergerak
klebih dekat ke awal untuk memilih nomor terkecil berikutnya.
Untuk masing-masing i, petunjuk dari jdan ksecara bertahap akan lebih dekat satu sama lain. Akhirnya mereka akan saling melewati, dan pada saat itu kita tidak perlu mencoba hal lain untuk itu i, karena kita akan menjumlahkan elemen yang sama, hanya dalam urutan yang berbeda. Setelah itu, kami mencoba yang berikutnya idan ulangi.
Akhirnya, kita akan menghabiskan kemungkinan yang berguna, atau kita akan menemukan solusinya. Anda dapat melihat bahwa ini adalah O (n 2 ) karena kami mengeksekusi loop luar O (n) kali dan kami mengeksekusi loop dalam O (n) kali. Dimungkinkan untuk melakukan ini secara sub-kuadrat jika Anda benar-benar suka, dengan mewakili setiap integer sebagai vektor bit dan melakukan transformasi Fourier cepat, tetapi itu di luar cakupan jawaban ini.
Catatan: Karena ini adalah pertanyaan wawancara, saya telah sedikit menipu: algoritma ini memungkinkan pemilihan elemen yang sama beberapa kali. Artinya, (-1, -1, 2) akan menjadi solusi yang valid, seperti halnya (0, 0, 0). Ia juga hanya menemukan jawaban yang tepat , bukan jawaban yang paling dekat, seperti judulnya. Sebagai latihan untuk pembaca, saya akan membiarkan Anda mencari tahu cara membuatnya bekerja dengan elemen yang berbeda saja (tapi itu perubahan yang sangat sederhana) dan jawaban yang tepat (yang juga merupakan perubahan yang sederhana).