Berikut O(N)
solusi sederhana yang menggunakan O(N)
ruang. Saya berasumsi bahwa kami membatasi daftar input ke bilangan non-negatif dan kami ingin mencari bilangan non-negatif pertama yang tidak ada dalam daftar.
- Temukan panjang daftar; katakanlah itu
N
.
- Alokasikan array
N
boolean, diinisialisasi ke semua false
.
- Untuk setiap angka
X
dalam daftar, jika X
kurang dari N
, setel X'th
elemen larik ke true
.
- Scan array mulai dari index
0
, cari elemen pertama yaitu false
. Jika Anda menemukan yang pertama false
di indeks I
, maka I
itulah jawabannya. Sebaliknya (yaitu ketika semua elemen true
) jawabannya adalah N
.
Dalam praktiknya, "array N
boolean" mungkin akan dikodekan sebagai "bitmap" atau "bitset" yang direpresentasikan sebagai array byte
atau int
. Ini biasanya menggunakan lebih sedikit ruang (tergantung pada bahasa pemrograman) dan memungkinkan pemindaian untuk yang pertama false
dilakukan lebih cepat.
Inilah bagaimana / mengapa algoritma bekerja.
Misalkan N
angka dalam daftar tidak berbeda, atau salah satu atau lebih dari angka tersebut lebih besar dari N
. Artinya, setidaknya harus ada satu angka dalam rentang 0 .. N - 1
yang tidak ada dalam daftar. Jadi masalah mencari bilangan hilang terkecil karenanya harus dikurangi menjadi masalah menemukan bilangan hilang terkecil kurang dariN
. Ini berarti kita tidak perlu melacak angka yang lebih besar atau sama denganN
... karena itu bukan jawabannya.
Alternatif dari paragraf sebelumnya adalah bahwa list tersebut merupakan permutasi dari bilangan-bilangan tersebut 0 .. N - 1
. Dalam kasus ini, langkah 3 menetapkan semua elemen array ke true
, dan langkah 4 memberi tahu kita bahwa nomor "hilang" pertama adalah N
.
Kompleksitas komputasi algoritma ini O(N)
dengan konstanta proporsionalitas yang relatif kecil. Itu membuat dua linier melewati daftar, atau hanya satu lulus jika panjang daftar diketahui untuk memulai. Tidak perlu mewakili seluruh daftar dalam memori, jadi penggunaan memori asimtotik algoritme adalah apa yang diperlukan untuk mewakili array boolean; yaitu O(N)
bit.
(Sebaliknya, algoritme yang mengandalkan penyortiran atau partisi dalam memori mengasumsikan bahwa Anda dapat mewakili seluruh daftar dalam memori. Dalam bentuk pertanyaan yang diajukan, ini akan membutuhkan O(N)
kata 64-bit.)
Komentar @Jorn bahwa langkah 1 hingga 3 adalah variasi dalam urutan penghitungan. Dalam arti tertentu dia benar, tetapi perbedaannya signifikan:
- Pengurutan penghitungan memerlukan larik (setidaknya)
Xmax - Xmin
penghitung Xmax
dengan angka terbesar dalam daftar dan Xmin
merupakan angka terkecil dalam daftar. Setiap penghitung harus dapat mewakili N status; yaitu mengasumsikan representasi biner itu harus memiliki tipe integer (setidaknya) ceiling(log2(N))
bit.
- Untuk menentukan ukuran larik, pengurutan penghitungan perlu melewati awal daftar untuk menentukan
Xmax
dan Xmin
.
- Oleh karena itu, persyaratan ruang kasus terburuk minimum adalah
ceiling(log2(N)) * (Xmax - Xmin)
bit.
Sebaliknya, algoritme yang disajikan di atas hanya membutuhkan N
bit dalam kasus terburuk dan terbaik.
Namun, analisis ini mengarah pada intuisi bahwa jika algoritme membuat awal melewati daftar mencari nol (dan menghitung elemen daftar jika diperlukan), itu akan memberikan jawaban yang lebih cepat tanpa menggunakan spasi sama sekali jika menemukan nol. Ini pasti layak dilakukan jika ada kemungkinan besar untuk menemukan setidaknya satu nol dalam daftar. Dan operan ekstra ini tidak mengubah keseluruhan kompleksitas.
EDIT: Saya telah mengubah deskripsi algoritme untuk menggunakan "array boolean" karena orang-orang tampaknya menganggap deskripsi asli saya menggunakan bit dan bitmap membingungkan.