Mengapa Python menggunakan tabel hash untuk mengimplementasikan dict, tetapi bukan Red-Black Tree?
Apa kuncinya? Performa?
Mengapa Python menggunakan tabel hash untuk mengimplementasikan dict, tetapi bukan Red-Black Tree?
Apa kuncinya? Performa?
Jawaban:
Ini adalah jawaban umum, non-Python-spesifik.
| Hash Table | Red-Black Tree |
-------+-------------+---------------------+
Space | O(n) : O(n) | O(n) : O(n) |
Insert | O(1) : O(n) | O(log n) : O(log n) |
Fetch | O(1) : O(n) | O(log n) : O(log n) |
Delete | O(1) : O(n) | O(log n) : O(log n) |
| avg :worst | average : worst |
Masalah dengan tabel hash adalah hash dapat bertabrakan. Ada berbagai mekanisme untuk menyelesaikan tabrakan, misalnya pengalamatan terbuka atau rantai terpisah. Kasus terburuk absolut adalah bahwa semua kunci memiliki kode hash yang sama, dalam hal ini tabel hash akan terdegradasi ke dalam daftar tertaut.
Dalam semua kasus lain, tabel hash adalah struktur data yang hebat yang mudah diimplementasikan dan memberikan kinerja yang baik. Kelemahannya adalah implementasi yang dapat dengan cepat menumbuhkan tabel dan mendistribusikan kembali entri mereka kemungkinan akan menghabiskan memori hampir sebanyak yang sebenarnya digunakan.
RB-Trees adalah penyeimbang diri dan tidak mengubah kompleksitas algoritmiknya dalam kasus terburuk. Namun, mereka lebih sulit diimplementasikan. Kompleksitas rata-rata mereka juga lebih buruk daripada tabel hash.
Semua kunci dalam tabel hash harus hashable dan sebanding untuk kesetaraan antara satu sama lain. Ini terutama mudah untuk string atau integer, tetapi juga cukup mudah untuk diperluas ke tipe yang ditentukan pengguna. Dalam beberapa bahasa seperti Java, properti ini dijamin oleh definisi.
Kunci dalam RB-Tree harus memiliki urutan total: masing-masing kunci harus dapat dibandingkan dengan kunci lainnya, dan kedua tombol harus membandingkan lebih kecil, lebih besar, atau sama. Kesetaraan pemesanan ini harus setara dengan kesetaraan semantik. Ini mudah untuk bilangan bulat dan angka lainnya, juga cukup mudah untuk string (urutan hanya perlu konsisten dan tidak dapat diamati secara eksternal, sehingga urutan tidak perlu mempertimbangkan lokal [1] ), tetapi sulit untuk jenis lain yang tidak memiliki urutan bawaan. . Sama sekali tidak mungkin untuk memiliki kunci dari tipe yang berbeda kecuali beberapa perbandingan di antara mereka adalah mungkin.
[1]: Sebenarnya, saya salah di sini. Dua string mungkin tidak sama dengan byte tetapi masih setara menurut aturan beberapa bahasa. Lihat misalnya normalisasi Unicode untuk satu contoh di mana dua string yang sama dikodekan secara berbeda. Apakah komposisi karakter Unicode penting untuk kunci hash Anda adalah sesuatu yang tidak diketahui oleh implementasi tabel hash.
Orang mungkin berpikir bahwa solusi murah untuk kunci RB-Tree adalah dengan terlebih dahulu menguji kesetaraan, kemudian membandingkan identitas (yaitu membandingkan pointer). Namun, pemesanan ini tidak akan transitif: Jika a == b
dan id(a) > id(c)
, maka harus mengikuti itu id(b) > id(c)
juga, yang tidak dijamin di sini. Jadi sebagai gantinya, kita mungkin menggunakan kode kunci hash sebagai kunci pencarian. Di sini, pemesanan bekerja dengan benar, tetapi kita mungkin berakhir dengan beberapa kunci berbeda dengan kode hash yang sama, yang akan ditugaskan ke simpul yang sama di pohon RB. Untuk mengatasi tabrakan hash ini kita bisa menggunakan rantai terpisah seperti halnya dengan tabel hash, tetapi ini juga mewarisi perilaku kasus terburuk untuk tabel hash - yang terburuk dari kedua dunia.
Saya berharap tabel hash memiliki memori lokalitas lebih baik daripada pohon, karena tabel hash pada dasarnya hanya sebuah array.
Entri di kedua struktur data memiliki overhead yang cukup tinggi:
Penyisipan dan penghapusan dalam pohon RB melibatkan rotasi pohon. Ini tidak terlalu mahal, tetapi melibatkan overhead. Dalam hash, penyisipan dan penghapusan tidak lebih mahal daripada akses sederhana (meskipun mengubah ukuran tabel hash setelah penyisipan adalah O(n)
upaya).
Tabel hash secara inheren bisa berubah, sedangkan RB-tree juga bisa diimplementasikan dengan cara yang tidak berubah. Namun, ini jarang bermanfaat.
Ada berbagai alasan yang mungkin benar, tetapi yang utama kemungkinannya adalah:
Lebih mudah untuk menulis / memelihara, dan pemenang kinerja dalam kasus penggunaan khusus? Tolong daftarkan saya!