---
# System prepended metadata

title: TP_ALPROG7_ReinathanEzkhielKurniawan_2406397675

---

# 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!