Diberikan array bilangan bulat, A 1 , A 2 , ..., A n , termasuk negatif dan positif, dan bilangan bulat lainnya S. Sekarang kita perlu menemukan tiga bilangan bulat yang berbeda dalam array, yang jumlahnya paling dekat dengan bilangan bulat S yang diberikan Jika ada lebih dari satu solusi, salah satunya tidak masalah.
Anda dapat mengasumsikan semua bilangan bulat berada dalam kisaran int32_t, dan tidak ada aritmatika overflow akan terjadi dengan menghitung jumlah. S tidak ada yang istimewa selain nomor yang dipilih secara acak.
Apakah ada algoritma yang efisien selain pencarian brute force untuk menemukan tiga bilangan bulat?
Jawaban:
Ya; kita bisa menyelesaikan ini dalam waktu O (n 2 )! Pertama, pertimbangkan bahwa masalah Anda
P
dapat diungkapkan dengan cara yang sedikit berbeda sehingga menghilangkan kebutuhan akan "nilai target":Perhatikan bahwa Anda dapat pergi dari versi ini masalah
P'
dariP
dengan mengurangkan S Anda / 3 dari setiap elemen dalamA
, 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:
Algoritma ini bekerja dengan menempatkan tiga pointer,
i
,j
, dank
pada berbagai titik dalam array.i
dimulai dari awal dan perlahan-lahan bekerja sampai akhir.k
menunjuk ke elemen terakhir.j
menunjuk ke tempati
dimulainya. Kami berulang mencoba untuk menjumlahkan elemen pada indeks masing-masing, dan setiap kali salah satu dari berikut terjadi:j
lebih dekat ke ujung untuk memilih nomor terbesar berikutnya.k
lebih dekat ke awal untuk memilih nomor terkecil berikutnya.Untuk masing-masing
i
, petunjuk darij
dank
secara bertahap akan lebih dekat satu sama lain. Akhirnya mereka akan saling melewati, dan pada saat itu kita tidak perlu mencoba hal lain untuk itui
, karena kita akan menjumlahkan elemen yang sama, hanya dalam urutan yang berbeda. Setelah itu, kami mencoba yang berikutnyai
dan 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).
sumber
tentu saja ini adalah solusi yang lebih baik karena lebih mudah dibaca dan karena itu kurang rentan terhadap kesalahan. Satu-satunya masalah adalah, kita perlu menambahkan beberapa baris kode untuk menghindari beberapa pilihan satu elemen.
Solusi O (n ^ 2) lainnya (dengan menggunakan hashset).
sumber
s2
mungkin elemen yang sudah dipilih. Misalnya jika array adalah0,1,2
danK
adalah2
, tidak boleh ada jawaban. Saya pikir algoritma Anda akan menampilkan0,1,1
yang jelas salah.Solusi John Feminella memiliki bug.
Di garis depan
Kita perlu memeriksa apakah i, j, k semuanya berbeda. Sebaliknya, jika elemen target saya adalah
6
dan jika array input saya berisi{3,2,1,7,9,0,-4,6}
. Jika saya mencetak tuple yang berjumlah 6, maka saya juga akan mendapatkan0,0,6
sebagai output. Untuk menghindari ini, kita perlu memodifikasi kondisi dengan cara ini.sumber
Bagaimana dengan sesuatu seperti ini, yaitu O (n ^ 2)
Ini menemukan jika jumlah 3 elemen persis sama dengan nomor Anda. Jika Anda ingin yang terdekat, Anda dapat memodifikasinya untuk mengingat delta terkecil (perbedaan antara jumlah triplet Anda saat ini) dan pada akhirnya cetak triplet yang sesuai dengan delta terkecil.
sumber
Perhatikan bahwa kami memiliki array yang diurutkan. Solusi ini mirip dengan solusi John hanya untuk mencari jumlah dan tidak mengulangi elemen yang sama.
sumber
a[r] + a[l] + a[i] - sum
. Cobalaharr = [-1, 2, 1, -4] sum = 1
.Berikut adalah kode C ++:
sumber
Solusi N ^ 2 * logN yang sangat sederhana: urutkan array input, lalu lewati semua pasangan A i , A j (N ^ 2 kali), dan untuk setiap pasangan periksa apakah (S - A i - A j ) dalam array ( waktu logN).
Solusi O (S * N) lainnya menggunakan pendekatan pemrograman dinamis klasik .
Pendeknya:
Buat array 2-d V [4] [S + 1]. Isi sedemikian rupa, sehingga:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 untuk semua i, V 1 [x] = 0 untuk semua x lainnya
V [2] [A i + A j ] = 1, untuk setiap i, j. V [2] [x] = 0 untuk semua x lainnya
V [3] [jumlah dari 3 elemen] = 1.
Untuk mengisinya, lakukan iterate melalui A i , untuk setiap A i iterate melalui array dari kanan ke kiri.
sumber
Ini dapat diselesaikan secara efisien dalam O (n log (n)) sebagai berikut. Saya memberikan solusi yang memberi tahu jika jumlah dari tiga angka sama dengan angka yang diberikan.
sumber
leftIndex
ataurightIndex
ketika semua elemen di tengah benar-benar lebih kecil atau lebih besar dari jumlah yang Anda inginkan. Tapi bagaimana dengan kasus ketika pencarian biner berhenti di suatu tempat di tengah? Anda perlu memeriksa kedua cabang (di manarightIndex--
danleftIndex++
). Dalam solusi Anda, Anda mengabaikan situasi ini. Tetapi saya tidak berpikir bahwa ada cara untuk mengatasi masalah ini.Pengurangan: Saya pikir solusi @John Feminella O (n2) paling elegan. Kita masih bisa mengurangi A [n] untuk mencari tuple. Dengan mengamati A [k] sedemikian rupa sehingga semua elemen akan berada dalam A [0] - A [k], ketika array pencarian kami sangat besar dan SUM (s) sangat kecil.
A [0] minimum: - Array terurut naik.
s = 2A [0] + A [k]: Diberikan s dan A [] kita dapat menemukan A [k] menggunakan pencarian biner dalam log (n) waktu.
sumber
Berikut ini adalah program dalam java yang O (N ^ 2)
sumber
Masalahnya dapat diselesaikan dalam O (n ^ 2) dengan memperluas masalah 2-sum dengan modifikasi kecil. A adalah vektor yang mengandung elemen dan B adalah jumlah yang diperlukan.
int Solution :: threeSumClosest (vektor & A, int B) {
sumber
Berikut adalah kode Python3
sumber
Solusi lain yang memeriksa dan gagal lebih awal:
Saya menambahkan beberapa tes unit di sini: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Jika set menggunakan terlalu banyak ruang yang saya dapat dengan mudah menggunakan java.util.BitSet yang akan menggunakan O (n / w) ruang .
sumber
Program untuk mendapatkan ketiga elemen tersebut. Saya baru saja mengurutkan array / daftar terlebih dahulu dan diperbarui
minCloseness
berdasarkan setiap triplet.sumber
Saya melakukan ini di n ^ 3, kodesemu saya di bawah ini;
// Buat hashMap dengan kunci sebagai Integer dan nilai sebagai ArrayList // iterate through list menggunakan for for, untuk setiap nilai dalam daftar iterate lagi mulai dari nilai berikutnya;
// jika jumlah arr [i] dan arr [j] kurang dari jumlah yang diinginkan maka ada potensi untuk menemukan digit ketiga jadi lakukan lagi untuk loop
// dalam hal ini kita sekarang mencari nilai ketiga; jika jumlah arr [i] dan arr [j] dan arr [k] adalah jumlah yang diinginkan kemudian tambahkan ini ke HashMap dengan membuat arr [i] kunci dan kemudian menambahkan arr [j] dan arr [k] ke dalam ArrayList dalam nilai kunci itu
setelah ini, Anda sekarang memiliki kamus yang memiliki semua entri yang mewakili tiga nilai yang ditambahkan ke jumlah yang diinginkan. Ekstrak semua entri ini menggunakan fungsi HashMap. Ini bekerja dengan sempurna.
sumber