Ini adalah pertanyaan pekerjaan rumah. Mereka mengatakan itu mengambil di O(logN + logM)
mana N
dan M
panjang lariknya.
Mari beri nama array a
dan b
. Jelas kita bisa mengabaikan semua a[i]
dan di b[i]
mana i> k.
Pertama mari kita bandingkan a[k/2]
dan b[k/2]
. Biarkan b[k/2]
> a[k/2]
. Oleh karena itu kita dapat membuang juga semua b[i]
, dimana i> k / 2.
Sekarang kita memiliki semua a[i]
, di mana i <k dan semua b[i]
, di mana i <k / 2 untuk menemukan jawabannya.
Apa langkah selanjutnya?
arrays
algorithm
binary-search
Michael
sumber
sumber
O(logN + logM)
hanya mengacu pada waktu yang dibutuhkan untuk mencari elemen k? Bisakah preprocessing dilakukan ke serikat terlebih dahulu?Jawaban:
Anda mengerti, lanjutkan saja! Dan hati-hati dengan indeks ...
Untuk menyederhanakannya, saya akan berasumsi bahwa N dan M adalah> k, jadi kompleksitasnya di sini adalah O (log k), yaitu O (log N + log M).
Kode semu:
Untuk demonstrasi Anda dapat menggunakan invarian loop i + j = k, tetapi saya tidak akan melakukan semua pekerjaan rumah Anda :)
sumber
Saya harap saya tidak menjawab pekerjaan rumah Anda karena sudah lebih dari setahun sejak pertanyaan ini diajukan. Berikut adalah solusi rekursif ekor yang akan membutuhkan waktu log (len (a) + len (b)).
Asumsi: Masukannya benar. yaitu k dalam rentang [0, len (a) + len (b)]
Kasus dasar:
Langkah-langkah pengurangan:
a
+ indeks tengahb
kurang darik
a
lebih besar dari elemen tengahb
, kita dapat mengabaikan paruh pertamab
, sesuaikank
.a
, sesuaikank
.k
kurang dari jumlah indeks tengaha
danb
:a
lebih besar dari elemen tengahb
, kita dapat mengabaikan paruh kedua dengan amana
b
Kode:
Harap dicatat bahwa solusi saya adalah membuat salinan baru dari array yang lebih kecil di setiap panggilan, ini dapat dengan mudah dihilangkan dengan hanya melewatkan indeks awal dan akhir pada array asli.
sumber
kthlargest()
mengembalikan(k+1)
elemen terkecil ke-2 misalnya,1
adalah elemen terkecil kedua0,1,2,3
yaitu, fungsi Anda kembalisorted(a+b)[k]
.Banyak orang menjawab pertanyaan "elemen terkecil ke-k dari dua larik terurut", tetapi biasanya hanya dengan ide-ide umum, bukan kode kerja yang jelas atau analisis kondisi batas.
Di sini saya ingin menguraikannya dengan hati-hati dengan cara saya pergi untuk membantu beberapa pemula untuk memahami, dengan kode Java saya yang berfungsi dengan benar.
A1
danA2
adalah dua array menaik yang diurutkan, dengansize1
dansize2
sebagai panjangnya masing-masing. Kita perlu mencari elemen terkecil ke-k dari gabungan kedua array tersebut. Di sini kami berasumsi bahwa(k > 0 && k <= size1 + size2)
, yang menyiratkan ituA1
danA2
tidak boleh keduanya kosong.Pertama, mari kita bahas pertanyaan ini dengan algoritma O (k) lambat. Caranya adalah dengan membandingkan elemen pertama dari kedua array,
A1[0]
danA2[0]
. Ambil yang lebih kecil, katakanA1[0]
ke saku kita. Kemudian bandingkanA1[1]
denganA2[0]
, dan seterusnya. Ulangi tindakan ini sampai kantong kita mencapaik
elemen. Sangat penting: Pada langkah pertama, kita hanya dapat berkomitmenA1[0]
di saku kita. Kami TIDAK dapat menyertakan atau mengecualikanA2[0]
!!!Kode O (k) berikut memberi Anda satu elemen sebelum jawaban yang benar. Di sini saya menggunakannya untuk menunjukkan ide saya, dan menganalisis kondisi batas. Saya memiliki kode yang benar setelah yang ini:
Ide yang paling kuat adalah bahwa di setiap loop, kami selalu menggunakan pendekatan kasus dasar. Setelah berkomitmen ke elemen terkecil saat ini, kita selangkah lebih dekat ke target: elemen terkecil ke-k. Jangan pernah melompat ke tengah dan membuat diri Anda bingung dan tersesat!
Dengan mengamati base case kode di atas
k == 1, k == size1+size2
, dan menggabungkannya dengan ituA1
danA2
keduanya tidak boleh kosong. Kita dapat mengubah logika menjadi gaya yang lebih ringkas di bawah ini.Ini adalah kode yang berfungsi lambat tapi benar:
Sekarang kita dapat mencoba algoritma yang lebih cepat berjalan pada O (log k). Demikian pula, bandingkan
A1[k/2]
denganA2[k/2]
; jikaA1[k/2]
lebih kecil, maka semua elemen dariA1[0]
sampaiA1[k/2]
harus ada di saku kita. Idenya adalah untuk tidak hanya berkomitmen pada satu elemen di setiap loop; langkah pertama mengandungk/2
elemen. Sekali lagi, kami TIDAK dapat menyertakan atau mengecualikanA2[0]
keA2[k/2]
. Jadi pada langkah pertama, kita tidak bisa lebih darik/2
elemen. Untuk langkah kedua, kita tidak bisa lebih darik/4
elemen ...Setelah setiap langkah, kita semakin mendekati elemen k-th. Pada saat yang sama setiap langkah menjadi semakin kecil, sampai kita mencapai
(step == 1)
, yaitu(k-1 == index1+index2)
. Kemudian kita bisa merujuk ke kasus dasar yang sederhana dan kuat lagi.Berikut adalah kode yang berfungsi dengan benar:
Beberapa orang mungkin khawatir bagaimana jika
(index1+index2)
melompati k-1? Bisakah kita melewatkan kasus dasarnya(k-1 == index1+index2)
? Itu tidak mungkin. Anda dapat menambahkan 0,5 + 0,25 + 0,125 ..., dan Anda tidak akan pernah melampaui 1.Tentu saja, sangat mudah untuk mengubah kode di atas menjadi algoritma rekursif:
Semoga analisis di atas dan kode Java dapat membantu Anda untuk memahami. Tapi jangan pernah menyalin kode saya sebagai pekerjaan rumah Anda! Bersulang ;)
sumber
else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0)
bukanelse if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)
? (Dalam kode kthSmallestSlowWithFault)Berikut adalah versi iteratif C ++ dari solusi @ lambdapilgrim (lihat penjelasan algoritme di sana):
Ia bekerja untuk semua
0 <= n < (size(a) + size(b))
indeks dan memilikiO(log(size(a)) + log(size(b)))
kompleksitas.Contoh
sumber
Upaya saya untuk nomor k pertama, nomor k dalam 2 larik yang diurutkan, dan dalam n larik yang diurutkan:
Kode lengkap dengan utilitas debug dapat ditemukan di: https://github.com/brainclone/teasers/tree/master/kth
sumber
Inilah kode saya berdasarkan solusi Jules Olleon:
sumber
Berikut adalah implementasi saya di C, Anda dapat merujuk ke penjelasan @Jules Olléon untuk algoritme: ide di balik algoritme adalah bahwa kita mempertahankan i + j = k, dan menemukan i dan j sehingga menjadi [i-1] <b [j-1] <a [i] (atau sebaliknya). Sekarang karena ada i elemen di 'a' lebih kecil dari b [j-1], dan elemen j-1 di 'b' lebih kecil dari b [j-1], b [j-1] adalah i + j-1 + 1 = elemen terkecil ke-k. Untuk menemukan i, j, algoritma melakukan pencarian dikotomis pada array.
sumber
Inilah solusi saya. Kode C ++ mencetak nilai terkecil ke-k serta jumlah iterasi untuk mendapatkan nilai terkecil ke-k dengan menggunakan loop yang menurut saya berada di urutan log (k). Namun kode tersebut membutuhkan k lebih kecil dari panjang array pertama yang merupakan batasan.
sumber
Kode pseudo pertama yang diberikan di atas, tidak berfungsi untuk banyak nilai. Misalnya, berikut adalah dua larik. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};
Itu tidak berhasil untuk k = 3 dan k = 9 di dalamnya. Saya punya solusi lain. Itu diberikan di bawah ini.
Tapi ... itu juga tidak berfungsi untuk k = 5. Ada tangkapan genap / ganjil dari k yang tidak membiarkannya menjadi sederhana.
sumber
sumber
Berikut solusi tambang di java. Akan mencoba untuk lebih mengoptimalkannya
Ini terinspirasi dari Algo di video youtube yang luar biasa
sumber
Tautan ke kode kompleksitas (log (n) + log (m))
Tautan ke Kode (log (n) * log (m))
Penerapan solusi (log (n) + log (m))
Saya ingin menambahkan penjelasan saya tentang masalah tersebut. Ini adalah masalah klasik di mana kita harus menggunakan fakta bahwa dua array diurutkan. kita telah diberikan dua array yang diurutkan arr1 dengan ukuran sz1 dan arr2 dengan ukuran sz2
a) Mari kita anggap jika
Memeriksa apakah k valid
maka kita tidak dapat menemukan elemen terkecil ke-k dalam gabungan kedua array yang diurutkan ryt Jadi kembalikan data yang tidak valid. b) Sekarang jika kondisi di atas bernilai salah dan kita memiliki nilai k yang valid dan layak,
Mengelola Kasus Edge
Kami akan menambahkan kedua array dengan nilai -tidak terhingga di depan dan + nilai tak terhingga di ujung untuk mencakup kasus tepi k = 1,2 dan k = (sz1 + sz2-1), (sz1 + sz2) dll.
Algoritma Utama
Sekarang, kita akan melakukan pencarian biner di arr1. Kita akan melakukan pencarian biner di arr1 mencari indeks i, startIndex <= i <= endIndex
sehingga jika kita menemukan indeks j yang sesuai di arr2 menggunakan kendala {(i + j) = k}, maka if
Karena kita tahu bahwa elemen terkecil ke-k memiliki (k-1) elemen yang lebih kecil darinya dalam penyatuan kedua array ryt? Begitu,
Dalam Kasus1 , apa yang kami lakukan, kami memastikan bahwa ada total (k-1) elemen yang lebih kecil ke arr1 [i] karena elemen yang lebih kecil dari arr1 [i] dalam array arr1 adalah i-1 jumlahnya dari yang kami ketahui (arr2 [ j-1] <arr1 [i] <arr2 [j]) dan jumlah elemen yang lebih kecil dari arr1 [i] di arr2 adalah j-1 karena j ditemukan menggunakan (i-1) + (j-1) = (k -1) Jadi elemen terkecil ke-k adalah arr1 [i]
Tetapi jawabannya mungkin tidak selalu datang dari array pertama yaitu arr1 jadi kami memeriksa case2 yang juga memenuhi sama seperti case 1 karena (i-1) + (j-1) = (k-1). Sekarang jika kita memiliki (arr1 [i-1] <arr2 [j] <arr1 [i]) kita memiliki total elemen k-1 yang lebih kecil dari arr2 [j] dalam penyatuan kedua array jadi itu elemen terkecil ke-k.
Dalam case3 , untuk membentuknya menjadi case 1 atau case 2, kita perlu menambahkan i dan j akan ditemukan sesuai dengan batasan {(i + j) = k} yaitu dalam pencarian biner pindah ke bagian kanan yaitu buat startIndex = middleIndex
Dalam case4 , untuk membentuknya menjadi case 1 atau case 2, kita perlu menurunkan i dan j akan ditemukan sesuai dengan batasan {(i + j) = k} yaitu dalam pencarian biner pindah ke bagian kiri yaitu make endIndex = middleIndex .
Sekarang bagaimana menentukan startIndex dan endIndex pada awal pencarian biner melalui arr1 dengan startindex = 1 dan endIndex = ??. Kita perlu memutuskan.
Karena jika k lebih besar dari ukuran array pertama, kita mungkin harus melakukan pencarian biner di seluruh array arr1, jika tidak kita hanya perlu mengambil k elemen pertama karena elemen sz1-k tidak pernah bisa berkontribusi dalam menghitung k terkecil.
KODE Di Bawah Ini
Untuk Solusi kompleksitas (log (n) * log (m))
Hanya saya melewatkan keuntungan dari fakta bahwa untuk setiap i j dapat ditemukan menggunakan kendala {(i-1) + (j-1) = (k-1)} Jadi untuk setiap ii selanjutnya menerapkan pencarian biner pada array kedua untuk menemukan j sehingga arr2 [j] <= arr1 [i]. Jadi solusi ini dapat dioptimalkan lebih lanjut
sumber
Pada dasarnya, melalui pendekatan ini Anda dapat membuang k / 2 elemen di setiap langkah. K akan berubah secara rekursif dari k => k / 2 => k / 4 => ... hingga mencapai 1. Jadi, Kompleksitas Waktu adalah O (logk)
Pada k = 1, kita mendapatkan yang terendah dari dua larik.
Kode berikut ada di JAWA. Harap dicatat bahwa kami mengurangi 1 (-1) dalam kode dari indeks karena indeks larik Java dimulai dari 0 dan bukan 1, misalnya. k = 3 diwakili oleh elemen dalam indeks ke-2 dari sebuah array.
sumber
Kompleksitas Waktu adalah O (log (min (n, m)))
sumber
Sebagian besar jawaban yang saya temukan di sini berfokus pada kedua larik. meskipun bagus tetapi lebih sulit untuk diterapkan karena ada banyak kasus tepi yang perlu kita tangani. Selain itu sebagian besar implementasinya bersifat rekursif yang menambah kompleksitas ruang stack rekursi. Jadi, alih-alih berfokus pada kedua larik, saya memutuskan untuk hanya fokus pada larik yang lebih kecil dan melakukan pencarian biner hanya pada larik yang lebih kecil dan menyesuaikan penunjuk untuk larik kedua berdasarkan nilai penunjuk di Array pertama. Dengan implementasi berikut, kita mendapatkan kompleksitas
O(log(min(n,m))
denganO(1)
kompleksitas ruang.Kami memiliki rentang
[low, high]
untuk larika
dan kami mempersempit rentang ini saat kami melangkah lebih jauh melalui algoritme.sizeA
menunjukkan berapa banyak item darik
item berasal dari arraya
dan itu berasal dari nilailow
danhigh
.sizeB
adalah definisi yang sama kecuali kita menghitung nilai sedemikian rupasizeA+sizeB=k
. Berdasarkan nilai-nilai pada kedua batas tersebut dengan kesimpulan bahwa kita harus memperluas ke sisi kanan dalam larika
atau menyusut ke sisi kiri. jika kita terjebak di posisi yang sama itu berarti kita menemukan solusi dan kita akan mengembalikan nilai maksimal di posisisizeA-1
daria
dansizeB-1
darib
.sumber
Periksa kode ini.
sumber
Di bawah kode C # untuk Menemukan Elemen Terkecil ke-k dalam Gabungan Dua Array yang Diurutkan. Kompleksitas Waktu: O (logk)
sumber
midA
dariendA
jikak < n
. Periksa larik pendek, dimulai denganreturn B[startB + k - 1];
.)