Kehendak soalan dan skop
Laksanakan jadual cincang pengalamatan terbuka berkapasiti tetap dengan tatasusunan m slot, di mana setiap slot memegang paling banyak satu pasangan kunci-nilai. Sokong insert(key,value), contains(key), dan remove(key). Selesaikan pertembungan dengan Robin Hood hashing; jangan gunakan perantaian (chaining) atau tombstone. Untuk menumpukan perhatian pada algoritma teras, jadual yang penuh boleh mengembalikan kegagalan dan bukannya mengubah saiz.
Ini ialah soalan temu duga pengekodan umum tentang struktur data, invarian, kes pinggir (edge cases), dan kekompleksan. Tugasan awam Stanford CS106B meminta pelajar melaksanakan jadual Robin Hood dan secara eksplisit merangkumi pertukaran jarak prob (probe-distance swapping), penamatan carian awal, dan pemadaman anjakan ke belakang (backward-shift deletion). Panduan temu duga kejuruteraan perisian semasa menyenaraikan pertimbangan struktur data, ketepatan, kekompleksan, dan pengendalian kes pinggir sebagai isyarat pengekodan.
Perkara yang diuji oleh penemu duga
- Bolehkah anda menyimpan baldi asal (home bucket) dan PSL, atau panjang jujukan prob (probe sequence length), bagi setiap elemen?
- Bolehkah anda menerangkan "kunci yang lebih miskin mendapat keutamaan": apabila PSL elemen yang masuk lebih besar, tukar tempat dengan kunci pemastautin yang lebih dekat dengan asalnya?
- Bolehkah anda menggunakan kemonotonan PSL untuk menghentikan carian yang gagal lebih awal dan bukannya mengimbas keseluruhan tatasusunan?
- Bolehkah anda memadam tanpa tombstone sambil memastikan setiap kunci dalam kelompok prob (probe cluster) kekal boleh dicapai?
- Bolehkah anda menyatakan kos purata dan kes terburuk serta memilih dasar untuk beban tinggi?
Jawapan biasa menulis linear probing tetapi terlepas pandang bahawa lubang pemadaman memotong laluan carian berikutnya. Jawapan yang kukuh mengubah kedua-dua "slot kosong" dan "PSL pemastautin di bawah PSL sasaran" menjadi syarat henti yang terbukti.
Penjelasan sebelum menjawab
- Adakah kapasiti tetap? Dengan kapasiti tetap, kegagalan penyisipan ialah hasil yang eksplisit; dengan saiz dinamik, ambang faktor beban akan mencetuskan pembinaan semula.
- Adakah kunci pendua dibenarkan? Andaikan kunci pendua mengemas kini nilainya dan bukannya menambah slot kedua; multimap memerlukan API dan kontrak pemadaman yang berbeza.
- Adakah cincangan stabil dan adakah kunci boleh disalin? Cincangan mestilah stabil sepanjang satu operasi. Menyimpan baldi asal dalam cache boleh mengelakkan kerja berulang tetapi menggunakan memori slot.
- Adakah lelaran (iterators) atau rujukan mesti kekal stabil? Pertukaran dan anjakan ke belakang menggerakkan elemen, jadi alamat yang stabil tidak dijamin. Gunakan penunjuk tidak langsung (indirection) jika pemanggil memerlukan pemegang yang stabil.
- Adakah konkurensi termasuk dalam skop? Ini adalah bebenang tunggal (single-threaded). Versi serentak memerlukan penguncian, penjaluran (striping), atau protokol tanpa kunci (lock-free); pelaksanaan biasa bukan thread-safe.
Rangka kerja jawapan 30 saat
"Saya menyimpan kunci, nilai, baldi asal, dan PSL dalam setiap slot yang diduduki. Penyisipan melakukan prob secara linear dari asal; apabila PSL yang masuk melebihi PSL pemastautin, saya menukar kedudukan mereka supaya elemen yang bergerak lebih jauh mendapat keutamaan, kemudian terus meletakkan item yang digantikan. Carian boleh gagal pada slot kosong atau apabila PSL pemastautin berada di bawah PSL sasaran, kerana entri berikutnya tidak boleh melompat kembali ke jarak yang lebih pendek. Pemadaman menganjak entri berikutnya ke belakang sehingga menemui slot kosong atau entri ber-PSL sifar, mengurangkan PSL bagi setiap pergerakan supaya tiada laluan carian yang terputus. Jangkaan operasi adalah hampir O(1), kes terburuk ialah O(m), dan ruang ialah O(m)."
Jawapan mendalam langkah demi langkah
1. Model slot dan invarian
Setiap slot yang diduduki menyimpan (key, value, home, psl). Dalam gelang m slot, psl = (index - home + m) % m. Kekalkan tiga invarian:
homeialah punca cincangan tetap bagi kunci tersebut.- Melangkah ke hadapan sebanyak
psllangkah darihomeakan mencapai indeks semasa. - Dalam satu kelompok prob yang berterusan, nilai PSL yang diduduki tidak pernah berkurangan; slot kosong menamatkan kelompok tersebut.
Invarian ketiga terhasil daripada pemberian keutamaan kepada PSL yang lebih besar semasa penyisipan. Ia membolehkan carian membandingkan PSL sasaran dengan PSL pemastautin dan bukannya memeriksa setiap slot berikutnya.
2. Kesesakan linear-probing
Linear probing biasa melangkah ke hadapan dari asal sehingga menemui slot kosong. Pada beban tinggi, kunci yang awal boleh menduduki slot yang dekat dengan asal manakala kunci kemudian yang telah mencari jauh terus melangkah; varians panjang prob kemudiannya meningkatkan kependaman ekor (tail latency). Robin Hood hashing mengekalkan reka letak tatasusunan yang padat tetapi memberikan keutamaan kepada kunci yang bergerak lebih jauh semasa pertembungan.
3. Penyisipan Robin Hood
Pseudokod:
insert(key, value):
item = (key, value, home=hash(key), psl=0)
for step in 0 .. m-1:
i = (item.home + item.psl) mod m
if table[i] is empty:
table[i] = item
return success
if table[i].key == key:
table[i].value = value
return updated
if table[i].psl < item.psl:
swap(table[i], item)
item.psl += 1
return fullSelepas pertukaran, item ialah entri yang digantikan. PSL-nya sudah menggambarkan kedudukan prob semasa, jadi lelaran seterusnya menambahnya sekali. Pastikan slot kosong dibezakan daripada entri sebenar yang mempunyai PSL sifar; jika tidak, sempadan penyisipan dan pemadaman menjadi kabur.
4. Carian dan penamatan awal
Carian bermula di punca sasaran dan menjejaki PSL sasaran:
contains(key):
home = hash(key)
for psl in 0 .. m-1:
i = (home + psl) mod m
if table[i] is empty:
return false
if table[i].psl < psl:
return false
if table[i].key == key:
return true
return falseSlot kosong menamatkan kelompok. PSL pemastautin yang berada di bawah sasaran bermakna slot berikutnya tidak boleh mengandungi sasaran, kerana PSL kelompok tidak berkurangan. Tugasan Stanford menganggap henti awal ini sebagai perbezaan teras daripada linear probing biasa.
5. Pemadaman anjakan ke belakang (backward-shift deletion)
Jangan kosongkan slot serta-merta: kunci yang kemudian mungkin telah melintasinya semasa penyelesaian pertembungan, dan carian akan berhenti secara salah pada lubang tersebut. Tombstone juga tidak dibenarkan dan akan memanjangkan prob dari semasa ke semasa.
remove(key):
i = find_index_or_not_found(key)
if i is not found:
return false
j = (i + 1) mod m
while table[j] is not empty and table[j].psl > 0:
table[i] = table[j]
table[i].psl -= 1
i = j
j = (j + 1) mod m
table[i] = empty
return trueBerhenti pada slot kosong atau entri ber-PSL sifar. Yang pertama menamatkan kelompok; yang kedua berada di asalnya, jadi mengosongkan slot sebelumnya tidak boleh memotong laluan cariannya. Setiap pergerakan mengurangkan PSL dan memulihkan invarian jarak.
6. Kekompleksan dan dasar beban tinggi
Dengan pencincangan seragam dan faktor beban α yang berada di bawah satu dengan selesa, jangkaan prob untuk insert, lookup, dan delete adalah pada skala malar. Satu operasi tunggal masih boleh mengimbas kesemua m slot, jadi masa kes terburuk ialah O(m) dan ruang ialah O(m). Robin Hood hashing terutamanya menambah baik taburan dan varians panjang prob; ia tidak menghapuskan kes terburuk pengalamatan terbuka. Analisis yang dipetik mengkaji varians terikat dalam model beban tinggi, tetapi kod pengeluaran masih memerlukan ambang beban.
Apabila α menghampiri ambang tersebut, bina semula pada kapasiti yang lebih besar daripada bergantung pada jangkaan O(1). Jika kapasiti mesti kekal tetap, anggap full sebagai hasil perniagaan biasa dan pantau kadar kegagalan, min PSL, prob P99, dan panjang anjakan pemadaman.
7. Contoh lawan dan ujian
- Penyisipan dan carian jadual kosong: slot asal diisi secara langsung, dan kunci yang tiada berhenti pada slot kosong pertama.
- Kunci pendua: mengemas kini nilai tidak menambah bilangan elemen.
- Balutan (wraparound): pilih punca berhampiran penghujung dan sahkan
(index - home + m) % m. - Rantaian pertukaran: bina kunci yang bertembung dan sahkan satu penyisipan boleh menggantikan dan meletakkan setiap item.
- Padam kepala, tengah, dan ekor kelompok: semua kunci yang tinggal kekal boleh dicari.
- Padam entri asal: berhenti apabila pengganti mempunyai PSL sifar, mengelakkan pergerakan merentas kelompok.
- Jadual penuh: kunci berbeza ke-
(m+1)mengembalikan kegagalan dan bukannya bergelung selama-lamanya. - Cincangan musuh (adversarial hash): petakan banyak kunci ke satu asal, sahkan ketepatan, dan dedahkan prob O(m) dalam metrik.
Contoh jawapan berkualiti tinggi
"Saya akan menggunakan jadual pengalamatan terbuka Robin Hood berkapasiti tetap, menyimpan kunci, nilai, dan PSL dalam setiap slot yang diduduki. Penyisipan melakukan prob secara linear dari asal. Jika entri yang masuk telah bergerak lebih jauh daripada entri pemastautin, saya menukar kedudukan mereka dan terus meletakkan entri yang digantikan. Ini mengekalkan PSL tidak menurun dalam satu kelompok prob.
"Carian menggunakan invarian tersebut: slot kosong gagal, dan PSL pemastautin di bawah PSL sasaran juga gagal kerana entri berikutnya tidak boleh kembali ke jarak yang lebih pendek. Pemadaman tidak boleh meninggalkan lubang, jadi saya menganjak entri ke belakang selagi PSL mereka positif dan mengurangkan setiap PSL; slot kosong atau entri ber-PSL sifar menamatkan anjakan. Masa jangkaan adalah hampir O(1), kes terburuk ialah O(m), jadi faktor beban, prob P99, dan panjang anjakan menentukan sama ada untuk mengubah saiz atau menolak penyisipan. Kerana anjakan menggerakkan elemen, saya tidak menjamin lelaran atau alamat yang stabil."
Kesilapan biasa
- Kesilapan → sentiasa mengekalkan pemastautin terdahulu semasa pertembungan → entri kemudian mengumpul prob yang panjang → tukar tempat apabila PSL yang masuk lebih besar.
- Kesilapan → menghentikan carian hanya pada slot kosong → kehilangan pengoptimuman PSL → berhenti juga apabila PSL pemastautin berada di bawah sasaran.
- Kesilapan → mengosongkan slot yang dipadam serta-merta → lubang memotong laluan prob kemudian → gunakan anjakan ke belakang dan kurangkan PSL.
- Kesilapan → berhenti menganjak pada mana-mana entri yang diduduki → meninggalkan kunci yang tidak boleh dicapai atau bergerak merentas kelompok → anjak hanya semasa PSL kelompok berterusan adalah positif.
- Kesilapan → menulis jangkaan O(1) sebagai kes terburuk O(1) → cincangan yang buruk dan beban tinggi membatalkannya → nyatakan kes terburuk O(m) dan kuat kuasakan ambang beban.
- Kesilapan → menjanjikan rujukan yang stabil → pertukaran dan pemadaman menggerakkan entri → kembalikan pemegang, gunakan penunjuk tidak langsung, atau gugurkan jaminan alamat.
Soalan susulan dan jawapan
Bilakah jadual yang berkembang secara dinamik patut dibina semula?
Cetuskan berdasarkan kedua-dua faktor beban dan kependaman prob ekor, seperti α yang dikonfigurasikan atau belanjawan prob P99. Kira semula setiap punca dan PSL semasa pembinaan semula; menyalin slot secara langsung adalah salah kerana modulus tatasusunan berubah. Kunci tulis, migrasi dwi-jadual, atau pembinaan semula latar belakang boleh memenuhi matlamat ketersediaan yang berbeza, tetapi nyatakan dasar ketekalan dan jeda terlebih dahulu.
Mengapakah tombstone dielakkan, dan bolehkah anjakan ke belakang menelan kos terlalu tinggi?
Tombstone menjadikan pemadaman O(1) tetapi memanjangkan carian secara kekal sehingga pembinaan semula dilakukan. Anjakan ke belakang menumpukan kerja pada operasi padam dan memastikan kelompok kekal padat. Jika pemadaman mendominasi dan pembacaan jarang berlaku, tombstone bersama pembinaan semula berkala boleh menjadi lebih baik; jika kependaman bacaan penting, utamakan anjakan dan pantau panjang pergerakan.
Bagaimanakah anda mengendalikan pembaca dan penulis serentak?
Pelaksanaan ini adalah bebenang tunggal (single-threaded). Versi serentak yang paling mudah menggunakan kunci baca-tulis (read-write lock); pertukaran dan anjakan mestilah satu bahagian genting tulis (write critical section) supaya pembaca tidak pernah melihat kelompok yang separa dialihkan. Daya pemprosesan yang lebih tinggi boleh menggunakan kunci berjalur (striped locks) atau snapshot tidak berubah (immutable snapshots). Reka bentuk tanpa kunci memerlukan perkataan versi, susunan memori, dan tebus guna; penunjuk slot atomik sahaja tidak mencukupi.
Adakah Robin Hood menjadikan purata carian masa malar?
Di bawah model cincangan seragam biasa, jangkaan kos pengalamatan terbuka bergantung pada faktor beban. Robin Hood terutamanya mengurangkan varians panjang prob dan sebaran ekor. Purata kos masih meningkat pada beban tinggi dan kes terburuk boleh mengimbas keseluruhan jadual, jadi peningkatan varians tidak menggantikan kawalan beban dan penanda aras.