# Tugas Pendahuluan Modul 7 - Hash Pembuat Soal: RE ``` Nama : Reinathan Ezkhiel Kurniawan NPM : 2406397675 ``` ## Teori (65 poin) ### 1. Jelaskan apa itu Hashing! Sebutkan dan jelaskan apa saja komponen yang ada dalam Hashing! <span style="color:red;"> (15 poin) </span> **Hashing** adalah sebuah teknik dalam struktur data yang melakukan pemetaan (*mapping*) dari sebuah kunci (*key*) ke indeks dalam sebuah tabel melalui fungsi *hash* sehingga operasi pencarian, penyisipan, dan penghapusan dapat dilakukan dengan waktu rata - rata **O(1)**. Komponen - komponen utama dalam *hashing* adalah : * **hash function** : fungsi yang menerima *key* sebagai *input* dan menghasilkan indeks (atau *bucket*) pada tabel. Metode paling umum adalah **division method** dengan formula **h(k) = k mod m** k adalah *key* dan m adalah *tabel*. * **hash table** : struktur penyimpanan dasar berupa *array* atau kumpulan *bucket* di mana hasil fungsi *hash* menentukan lokasi untuk menyimpan *value* atau pasangan *key-value*. * **Collision resolution mechanism** : karena dua atau lebih *key* bisa menghasilkan indeks yang sama (*collision*), maka diperlukan mekanisme untuk menangani hal ini. (Contohnya di Nomor 2). * *Load Factor* merupakan antara jumlah elemen yang tersimpan dengan jumlah *bucket*. Penting karena mempengaruhi performa dan dan menentukan saat untuk melakukan *rehashing*. * **rehashing** terjadi bila *load factor* terlalu besar maka ukuran tabel diperbesar (misal di*double*) dan semua elemen di - *rehash* (dimasukkan ulang) ke tabel yang lebih besar agar performa tetap baik. ### Referensi: GeeksforGeeks, “Hashing in Data Structure,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/hashing-data-structure/. [Diakses: 7-Nov-2025] GeeksforGeeks, “Hash Table Data Structure,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/hash-table-data-structure/. [Diakses: 7-Nov-2025] --- ### 2. Jelaskan apa itu collision dalam hashing! Sebutkan dan jelaskan cara yang umum digunakan untuk mencegah terjadinya collision! <span style="color:red;"> (15 poin) </span> *Collision* terjadi ketika dua atau lebih *key* berbeda dipetakan oleh fungsi *hash* ke indeks atau *bucket* yang sama dalam tabel *hash*. Cara umum untuk mencegah *collision* : * Gunakan **separate chaining**. Setiap *bucket* dalam *hash table* bukan hanya menyimpan satu elemen tapi sebuah daftar (**linked list**) atau truktur lainnya yang menyimpan semua elemen yang *hash*- nya sama. Jika terjadi *collision*, elemen baru tinggal ditambahkan ke *chain* di *bucket* yang sama. * **Open addressing** dengan menyimpan langsung semua elemen dalam *hash table* (jika tidak ada *list* eksternal). bila tempat yang dihasilkan fungsi *hash* sudah terisi, maka dilakukan **probing** (mencari slot kosong berikutnya) dengan berbagai strategi seperti **linear probing**, **quadratic probing** dan **double hashing** (menggunakan *hash* kedua sebagai *offset* dalam *probing*). * **Coalesced hashing** adalah kombinasi antara *chaining* dan *open addressing* saat terjadi *collision*, elemen ditempatkan di slot kosong yang ditemukan dan kemudian terhubung dalam suatu *chain* di dalam tabel itu sendiri. ### Referensi: GeeksforGeeks, “Collision Resolution Techniques,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/collision-resolution-techniques/ . [Diakses: 7-Nov-2025] GeeksforGeeks, “Separate Chaining Collision Handling Technique in Hashing,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/separate-chaining-collision-handling-technique-in-hashing/ . [Diakses: 7-Nov-2025] --- ### 3. Jelaskan apa itu Load Factor! Mengapa konsep rehashing menjadi penting? <span style="color:red;"> (10 poin) </span> *Load Factor* pada *hash table* merupakan rasio antara jumlah elemen yang tersimpan (**n**) dengan jumlah **bucket** atau **slot** dalam tabel (**m**) maka *Load Factor* = **n**/**m**. Semakin besar *Load Factor* maka semakin banyak elemen yang saling bersaing untuk *bucket* yang sama (meningkatkan *collision*, dan waktu pencarian rata-rata menjadi lebih besar dari O(1)). Dengan memantau *load factor* dan menetapkan **threshold** maka sistem dapat melakukan *rehashing* (memperbesar tabel dan me-*rehash* elemen) agar tabel tetap memiliki *load factor* yang wajar dan menjaga performa tetap dalam jangkauan kompleksitas **O(1)**. Maka, konsep *rehashing* menjadi penting karena ketika *load factor* sudah melewati batas yang ditetapkan, maka tabel perlu di *re-size* dan semua elemen dipetakan ulang ke *bucket* baru agar distribusi *key* lebih baik dan *collision* lebih sedikit. Tanpa *rehashing* maka performa struktur *hash* akan menurun drastis. ### Referensi: GeeksforGeeks, “Load Factor and Rehashing,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/load-factor-and-rehashing/. [Diakses: 7-Nov-2025] GeeksforGeeks, “Introduction to Hashing,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/introduction-to-hashing-2/. [Diakses: 7-Nov-2025] --- ### 4. Jelaskan mengenai template `std::unordered_map` dan `std_unordered_set` pada library STL! Apa perbedaan diantara keduanya? <span style="color:red;"> (15 poin) </span> `std::unordered_map< Key, T>` : kontainer asosiasi yang tak berurut. Kontainer menyimpan pasangan *key-value*. Implementasi dasarnya menggunakan *hash table* sehingga rata-rata waktu untuk operasi *insert*,*find*,*erase* adalah O(1). `std::unordered_set< Key, T>` adalah kontainer asosiasi yang tak berurut yang menyimpan hanya *key* dan tidak menyimpan *mapped value*. Juga berbasis *hash table*, artinya operasi *insert*,*find*,*erase* rata - rata O(1). Perbedaan nya adalah : * `unordered_map` menyimpan pasangan *key-value* sedangkan `unordered_set` hanya menyimpan *key*. * `unordered_map` lebih cocok untuk struktur data seperti `dictionary` dimana kita ingin akses berdasarkan *key* untuk mendapatkan *value*. `unordered_set` cocok untuk koleksi unik elemen yang hanya ingin kita cek keberadaanya. * Pada `unordered_map` terdapat *method* untuk mengambil/ mengubah **mapped value** (`map[key]`, `at(key)`), sedangkan `unordered_set` hanya menyediakan *method* untuk *insert*,*find*,*erase* elemen *key* saja. * Penggunaan `unordered_map` akan menyebabkan *overhead* memori sedikit lebih besar daripada `unordered_set` karena menyimpan *value*. ### Referensi: GeeksforGeeks, “Unordered Map in C++ STL,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/cpp/unordered_map-in-cpp-stl/. [Diakses: 7-Nov-2025] - GeeksforGeeks, “Unordered Sets in C++ STL,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/cpp/unordered_set-in-cpp-stl/. [Diakses: 7-Nov-2025] --- ### 5. Mengapa searching menggunakan Hash Map biasanya lebih cepat daripada algoritma searching konvensional (Linear Search, Binary Search)? Jelaskan! <span style="color:red;"> (10 poin) </span> *Searching* menggunakan *hash map* seperti `std::unordered_map` yang umumnya lebih cepat karena : * Waktu akses rata - rata untuk operati pencarian di *hash table* adalah **O(1)**. Karena kita langsung menggunakan *hash function* untuk mendapatkan indeks *bucket* dan kemudian mencari elemen. * Sementara algoritma pencarian konvensional **lienar search** memiliki waktu **O(n)** karena harus memerika satu per satu elemen hingga ditemukan. * *Binary Search* memiliki waktu O(log n) tetapi memerlukan data yang sudah diurutkan dan akses acak ke elemen (masih lebih lambat daripada *hash table* apabila berfungsi dengan baik). * Karena *hash table* tidak bergantung pada **urutan** dan melakukan perhitungan *hash* kemudian langsung akses, maka untuk banyak kasus penggunaan di mana kita hanya perlu menentukan eksistensi *key* dan *value*, maka *Hash Map* memberikan performa yang sangat baik. ### Referensi: GeeksforGeeks, “Hash Table Data Structure,” GeeksforGeeks. [Online]. Available: https://www.geeksforgeeks.org/dsa/hash-table-data-structure/. [Diakses: 7-Nov-2025] Programiz, “Hash Table Data Structure,” Programiz. [Online]. Available: https://www.programiz.com/dsa/hash-table. [Diakses: 7-Nov-2025] --- ## Praktik (35 poin) Mr. Adolf adalah seorang dosen Sastra Mesin di universitas terkemuka di Jerman. Ia ingin membuat database nilai yang efisien **untuk menyimpan dan mencari data mahasiswa dengan cepat**. Mr. Adolf sudah membuat kerangka kode dari sistem database yang ia inginkan. Akan tetapi, ia sedang sibuk menghadiri konferensi di universitas seni di Austria. Kamu adalah **asdos** dari Mr. Adolf, dan beliau meminta kamu untuk mengimplementasikan **logika hashing** menggunakan C++ tanpa STL. ### Lengkapi kode di bawah! <span style="color:red;"> (20 poin) </span> Gunakan nama dan NPM mahasiswa sekreatif mungkin untuk menghindari plagiarisme ```cpp #include <iostream> #include <string> #include <vector> #include <list> #include <functional> struct Mahasiswa { std::string nama; long long npm; }; bool operator==(const Mahasiswa& a, const Mahasiswa& b) { return (a.npm == b.npm) && (a.nama == b.nama); } struct KeyValuePair { Mahasiswa key; int value; KeyValuePair(const Mahasiswa& k, int v) : key(k), value(v) {} }; class MyHashMap { private: int n; int m; std::vector<std::list<KeyValuePair>> table; const float MAX_LOAD_FACTOR = 0.75; int hashFunction(Mahasiswa key) { unsigned long long HashMhs = 0; const unsigned long long PrimeNum = 131; for (char c : key.nama) { HashMhs = HashMhs * PrimeNum + static_cast<unsigned char>(c); } HashMhs = HashMhs * PrimeNum + static_cast<unsigned char>(key.npm); return static_cast<int>(HashMhs % m); } void rehash() { int MLama = m; m = m * 2; std::vector<std::list<KeyValuePair>> oldTable = table; table.clear(); table.resize(m); n = 0; for (int i = 0; i < MLama ; i++) { for (const auto& KV : oldTable[i]) { insert(KV.key, KV.value); } } } public: MyHashMap(int size = 10) { n = 0; m = size; table.resize(m); } void insert(Mahasiswa key, int value) { if ((float)(n + 1) / m > MAX_LOAD_FACTOR) { rehash(); } int index = hashFunction(key); auto& chain = table[index]; for(auto& KV : chain) { if(KV.key == key) { KV.value = value; return; } } chain.push_back(KeyValuePair(key, value)); n++; } int search(Mahasiswa key) { int index = hashFunction(key); auto& chain = table[index]; for(auto& KV : chain) { if(KV.key == key) { return KV.value; } } return -1; } void remove(Mahasiswa key) { int index = hashFunction(key); auto& chain = table[index]; for (auto it = chain.begin(); it != chain.end(); ++it) { if(it -> key == key) { chain.erase(it); n--; return; } } } }; int main() { MyHashMap studentMap; Mahasiswa Rei = {"Reinathan Ezkhiel", 2406397601LL}; Mahasiswa Coki = {"Reyhan Batara", 2406397602LL}; Mahasiswa Otniel = {"Otniel Sianturi", 2406397603LL}; Mahasiswa Josh = {"Joshua Ricardo", 2406397603LL}; studentMap.insert(Rei, 92); studentMap.insert(Coki, 85); studentMap.insert(Otniel, 78); studentMap.insert(Josh, 67); std::cout << "Nilai Reinathan Ezkhiel: " << studentMap.search(Rei) << std::endl; std::cout << "Nilai Reyhan Batara: " << studentMap.search(Coki) << std::endl; std::cout << "Nilai Otniel Sianturi: " << studentMap.search(Otniel) << std::endl; std::cout << "Nilai Joshua Ricardo: " << studentMap.search(Otniel) << std::endl; studentMap.insert(Rei, 30); studentMap.insert(Coki, 45); studentMap.insert(Otniel, 18); studentMap.insert(Josh, 27); std::cout << "Nilai Reinathan Ezkhiel (update): " << studentMap.search(Rei) << std::endl; std::cout << "Nilai Reyhan Batara (update): " << studentMap.search(Coki) << std::endl; std::cout << "Nilai Otniel Sianturi(update): " << studentMap.search(Otniel) << std::endl; std::cout << "Nilai Joshua Ricardo(update): " << studentMap.search(Otniel) << std::endl; studentMap.remove(Otniel); studentMap.remove(Coki); std::cout << "Nilai Otniel Sianturi setelah dihapus (harus -1): " << studentMap.search(Otniel) << std::endl; std::cout << "Nilai Reyhan Batara setelah dihapus (harus -1): " << studentMap.search(Coki) << std::endl; return 0; } ``` **Screenshot Output:** ![image](https://hackmd.io/_uploads/B1Yhz2oJWe.png) --- ### Refactor Kode <span style="color:red;"> (10 poin) </span> Mr. Adolf sudah menerima program database nilai yang kamu buat. Akan tetapi, beliau penasaran dengan perbedaan implementasi **separate chaining** dengan **open addressing**. Oleh karena itu, Mr. Adolf meminta kamu untuk **mengubah** program yang kamu buat untuk mengimplementasikan open addressing dengan **linear probing**. **Program Baru:** ```cpp #include <iostream> #include <string> #include <vector> struct Mahasiswa { std::string nama; long long npm; }; bool operator==(const Mahasiswa& a, const Mahasiswa& b) { return (a.npm == b.npm) && (a.nama == b.nama); } struct Entry { Mahasiswa key; int value; bool terisi; bool dihapus; Entry() : value(0), terisi(false), dihapus(false) {} }; class MyHashMap { private: int n; int m; std::vector<Entry> table; const float MAX_LOAD_FACTOR = 0.75; int hashFunction(Mahasiswa key) { unsigned long long HashMhs = 0; const unsigned long long PrimeNum = 131; for (char c : key.nama) { HashMhs = HashMhs * PrimeNum + static_cast<unsigned char>(c); } HashMhs = HashMhs * PrimeNum + static_cast<unsigned char>(key.npm); return static_cast<int>(HashMhs % m); } void rehash() { int MLama = m; m = m * 2; std::vector<Entry> oldTable = table; table.clear(); table.resize(m); n = 0; for (int i = 0; i < MLama ; i++) { if (oldTable[i].terisi && !oldTable[i].dihapus) { insert(oldTable[i].key, oldTable[i].value); } } } public: // Constructor MyHashMap(int size = 10) { n = 0; m = size; table.resize(m); } void insert(Mahasiswa key, int value) { // Cek load factor if (static_cast<float>(n + 1) / m > MAX_LOAD_FACTOR) { rehash(); } int index = hashFunction(key); int firstDeleted = -1; for (int i = 0; i < m; ++i) { int current = (index + i)%m; Entry& e = table[current]; if(e.terisi && e.dihapus && firstDeleted == -1) { firstDeleted = current; } if(!e.terisi) { int target = (firstDeleted != -1) ? firstDeleted : current; table[target].key = key; table[target].value = value; table[target].terisi = true; table[target].dihapus = false; n++; return; } if(!e.dihapus && e.key == key) { e.value = value; return; } } } int search(const Mahasiswa& key){ int index = hashFunction(key); for(int i = 0; i < m; ++i) { int current = (index + i) % m; const Entry& e = table[current]; if(!e.terisi && !e.dihapus) { return -1; } if(!e.terisi && !e.dihapus && e.key == key) { return e.value; } } } void remove(Mahasiswa key) { int index = hashFunction(key); for (int i = 0; i < m; ++i) { int current = (index + i) % m; Entry& e = table[current]; if(!e.terisi && !e.dihapus) { return; } if(e.terisi && !e.dihapus && e.key == key) { e.dihapus = true; n--; return; } } } }; int main() { MyHashMap studentMap; Mahasiswa Rei = {"Reinathan Ezkhiel", 2406397601LL}; Mahasiswa Coki = {"Reyhan Batara", 2406397602LL}; Mahasiswa Otniel = {"Otniel Sianturi", 2406397603LL}; Mahasiswa Josh = {"Joshua Ricardo", 2406397603LL}; studentMap.insert(Rei, 92); studentMap.insert(Coki, 85); studentMap.insert(Otniel, 78); studentMap.insert(Josh, 67); std::cout << "Nilai Reinathan Ezkhiel: " << studentMap.search(Rei) << std::endl; std::cout << "Nilai Reyhan Batara: " << studentMap.search(Coki) << std::endl; std::cout << "Nilai Otniel Sianturi: " << studentMap.search(Otniel) << std::endl; std::cout << "Nilai Joshua Ricardo: " << studentMap.search(Otniel) << std::endl; studentMap.insert(Rei, 30); studentMap.insert(Coki, 45); studentMap.insert(Otniel, 18); studentMap.insert(Josh, 27); std::cout << "Nilai Reinathan Ezkhiel (update): " << studentMap.search(Rei) << std::endl; std::cout << "Nilai Reyhan Batara (update): " << studentMap.search(Coki) << std::endl; std::cout << "Nilai Otniel Sianturi(update): " << studentMap.search(Otniel) << std::endl; std::cout << "Nilai Joshua Ricardo(update): " << studentMap.search(Otniel) << std::endl; studentMap.remove(Otniel); studentMap.remove(Coki); std::cout << "Nilai Otniel Sianturi setelah dihapus (harus -1): " << studentMap.search(Otniel) << std::endl; std::cout << "Nilai Reyhan Batara setelah dihapus (harus -1): " << studentMap.search(Coki) << std::endl; return 0; } ``` **Screenshot Output:** ![image](https://hackmd.io/_uploads/Bycft2jkbe.png) --- ### Sebutkan dan jelaskan perubahan apa saja yang kamu lakukan pada kode baru! <span style="color:red;"> (5 poin) </span> * Menghapus Struktur List (*Chaining*) : sebelumnya setiap indeks tabel menyimpan `std::list<KeyValuePair>` untuk menampung beberapa elemen jika terjadi *collision*. Pada *refactor*, setiap indeks hanya menyimpan satu objek `Entry`, sehingga tidak perlu *list* lagi. * Diperlukan penanda slot dihapus dalam *open addressing* agar `search` tetap bisa melanjutkan *probing* setelah penghapusan (tambahan atribut `dihapus` di `struct Entry`). * Diganti menjadi: ```cpp struct Entry { Mahasiswa key; int value; bool terisi; bool dihapus; }; ``` * Mengubah Mekanisme **Collision Handling** pada sebelumnya, jika terjadi tabrakan, data baru dimasukkan ke *list* (*chaining*).Sekarang menggunakan *linear probing ```cpp for (int i = 0; i < m; ++i) { int current = (index + i) % m; } ``` * Penyesuaian Operasi `insert()`,`search()`, dan `remove()` * **Insert**:Jika key sudah ada maka **update** `value`. Jika slot penuh,lanjut `probing`. Jika *load factor* melebihi batas → panggil `rehash()`. * **Search**: Lanjut *probing* hingga menemukan `key` yang cocok atau slot kosong murni (`terisi == false` dan `dihapus == false`). * **Remove**: Tidak menghapus fisik slot, tapi hanya menandainya sebagai `dihapus = true` (**tombstone**). * Sekarang `rehash()` menyalin seluruh **Entry aktif** ke tabel baru berukuran dua kali lebih besar. Tidak perlu memindahkan *list*, cukup reinsert data yang *terisi && !dihapus*. * `std::vector<std::list<KeyValuePair>>` diubah menjadi `std::vector<Entry>`. * Jumlah bucket tetap m, tapi setiap bucket hanya berisi satu elemen. --- ## <span style="color:red;">NOTE:</span> Pelajari `std::unordered_map`, `std::unordered_set`, penggunaan library `sstream`, dan penggunaan iterator untuk mempermudah pengerjaan CS!