Pertemuan 14
Universitas Pertahanan RI
Setelah pertemuan ini, mahasiswa mampu:
Permasalahan: Menyimpan & mengakses data personel berdasarkan NIK
| Struktur Data | Search | Masalah |
|---|---|---|
| Array unsorted | O(n) | Terlalu lambat |
| Array sorted | O(log n) | Insert/Delete lambat |
| BST balanced | O(log n) | Bisa tidak seimbang |
| Hash Table | O(1) | โ Ideal! |
Ide: Gunakan key sebagai index array langsung
int table[1000];
// Insert
table[key] = value; // O(1)
// Search
return table[key]; // O(1)
// Delete
table[key] = -1; // O(1)
โ ๏ธ Masalah: NIK 16 digit โ perlu array 10^16 slot!
Hashing adalah teknik memetakan key dari domain besar ke domain lebih kecil menggunakan fungsi hash h(k).
Gambar 14.1: Konsep dasar hashing
๐ฆ Array
Ukuran m (bucket/slot)
โ๏ธ Fungsi Hash
h: U โ {0,1,...,m-1}
๐ง Collision Handling
Jika h(kโ) = h(kโ)
h(k) = k mod m
int hashDivision(int key, int m) {
return key % m;
}
// h(12345) dengan m = 97
// = 12345 % 97 = 70
๐ก Tips: Pilih m = bilangan prima (97, 193, 389, ...)
h(k) = floor(m ร (k ร A mod 1))
A โ 0.618 (golden ratio)
int hashMultiplication(int key, int m) {
const double A = 0.6180339887;
double temp = key * A;
temp = temp - (int)temp; // Bagian fraksional
return (int)(m * temp);
}
Keuntungan: Nilai m tidak kritis, bisa power of 2
Polynomial Rolling Hash:
unsigned long hashString(const string& key, int m) {
unsigned long hash = 0;
unsigned long base = 31; // Bilangan prima
for (char c : key) {
hash = (hash * base + c) % m;
}
return hash;
}
// hash("abc", 100) = 54
Mengapa m = 100 bukan pilihan baik untuk division method?
Karena m = 100 = 10ยฒ hanya menggunakan 2 digit terakhir key:
Solusi: Gunakan bilangan prima (97, 101)
Collision terjadi ketika dua key berbeda kโ โ kโ menghasilkan hash value sama: h(kโ) = h(kโ)
Gambar 14.2: Ilustrasi collision
Pigeonhole Principle:
Jika n item dimasukkan ke m slot (n > m), minimal satu slot berisi lebih dari satu item.
Birthday Paradox:
Dalam grup 23 orang, P(2 orang birthday sama) > 50%!
| Pendekatan | Teknik | Ide Dasar |
|---|---|---|
| Separate Chaining | Linked List | Chain di setiap slot |
| Open Addressing | Probing | Cari slot kosong lain |
h(k) = k mod 7
| Key | Hash | Status |
|---|---|---|
| 3 | 3 mod 7 = 3 | OK |
| 10 | 10 mod 7 = 3 | Collision! |
| 17 | 17 mod 7 = 3 | Collision! |
| 24 | 24 mod 7 = 3 | Collision! |
Pola: Semua key bentuk 7k + 3 collision di slot 3
Separate Chaining menyimpan semua key dengan hash sama dalam linked list di slot tersebut.
Gambar 14.3: Hash table dengan separate chaining
void insert(int key, const string& value) {
int index = hash(key);
// Cek apakah key sudah ada
for (auto& pair : table[index]) {
if (pair.first == key) {
pair.second = value; // Update
return;
}
}
// Key belum ada, tambahkan
table[index].push_back({key, value});
}
// Search
string* search(int key) {
int index = hash(key);
for (auto& pair : table[index]) {
if (pair.first == key) {
return &pair.second;
}
}
return nullptr; // Not found
}
// Delete
bool remove(int key) {
int index = hash(key);
for (auto it = table[index].begin(); it != table[index].end(); ++it) {
if (it->first == key) {
table[index].erase(it);
return true;
}
}
return false;
}
| Operasi | Average | Worst Case |
|---|---|---|
| Insert | O(1) | O(n) |
| Search | O(1 + ฮฑ) | O(n) |
| Delete | O(1 + ฮฑ) | O(n) |
ฮฑ = n/m adalah load factor
Open Addressing menyimpan semua elemen dalam array. Jika slot terisi, cari slot kosong dengan probing.
| Aspek | Separate Chaining | Open Addressing |
|---|---|---|
| Struktur tambahan | Linked list | Tidak ada |
| Load factor max | > 1 | โค 1 |
| Cache performance | Buruk | Baik |
h(k, i) = (h'(k) + i) mod m
i = 0, 1, 2, 3, ... (nomor percobaan)
Gambar 14.4: Linear probing
bool insert(int key, const string& value) {
int index = hash(key);
int i = 0;
do {
int probeIndex = (index + i) % size;
// Slot kosong - bisa insert
if (!occupied[probeIndex]) {
keys[probeIndex] = key;
values[probeIndex] = value;
occupied[probeIndex] = true;
return true;
}
// Linear probe: cek slot berikutnya
i++;
} while (i < size);
return false; // Table penuh
}
Primary Clustering: Terbentuknya cluster (kelompok slot terisi berurutan) yang semakin besar.
Jika ada cluster panjang k:
h(k, i) = (h'(k) + cโยทi + cโยทiยฒ) mod m
Bentuk sederhana: h(k, i) = (h'(k) + iยฒ) mod m
Probe sequence untuk h'(k) = 5, m = 11:
| i | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| Slot | 5 | 6 | 9 | 3 | 10 |
h(k, i) = (hโ(k) + i ยท hโ(k)) mod m
int h1(int k) { return k % m; }
int h2(int k) { return 1 + (k % (m-1)); }
int doubleHash(int k, int i, int m) {
return (h1(k) + i * h2(k)) % m;
}
โ Menghindari clustering
โ Distribusi probe lebih merata
Insert key 10, 22, 31, 4, 15 ke hash table m=11, h(k)=k mod 11.
Di mana posisi akhir key 15?
| Key | h(k) | Probes | Position |
|---|---|---|---|
| 10 | 10 | 10 | 10 |
| 22 | 0 | 0 | 0 |
| 31 | 9 | 9 | 9 |
| 4 | 4 | 4 | 4 |
| 15 | 4 | 4โ5 | 5 |
Masalah: Hard delete di open addressing merusak probe sequence!
Solusi: Tandai slot sebagai "DELETED" bukan "EMPTY"
Load Factor (ฮฑ) = n / m
Rasio jumlah elemen terhadap ukuran tabel
| ฮฑ | Interpretasi | Performa |
|---|---|---|
| 0.25 | 25% terisi | Sangat baik |
| 0.50 | 50% terisi | Baik |
| 0.75 | 75% terisi | Threshold standar |
| 0.90 | 90% terisi | Degradasi |
Rehashing adalah proses membuat hash table baru lebih besar dan memindahkan semua elemen.
Kapan rehashing?
Contoh: ฮฑ = 0.8 โ rehash 2ร โ ฮฑ baru = 0.4
| ฮฑ | Unsuccessful | Successful |
|---|---|---|
| 0.50 | 2 probes | 1.39 probes |
| 0.75 | 4 probes | 1.85 probes |
| 0.90 | 10 probes | 2.56 probes |
| 0.95 | 20 probes | 3.15 probes |
Kesimpulan: Jaga ฮฑ โค 0.75 untuk performa baik!
| Struktur Data | Search | Insert | Delete |
|---|---|---|---|
| Array (unsorted) | O(n) | O(1) | O(n) |
| Array (sorted) | O(log n) | O(n) | O(n) |
| BST (balanced) | O(log n) | O(log n) | O(log n) |
| Hash Table | O(1) avg | O(1) avg | O(1) avg |
#include <unordered_map>
// Menghitung frekuensi karakter
string text = "hello world";
unordered_map<char, int> freq;
for (char c : text) {
freq[c]++; // O(1) per karakter
}
// freq['l'] = 3, freq['o'] = 2, ...
vector<int> findIntersection(const vector<int>& arr1,
const vector<int>& arr2) {
// Masukkan arr1 ke hash set - O(n)
unordered_set<int> set1(arr1.begin(), arr1.end());
// Cek arr2 yang ada di set1 - O(m)
vector<int> result;
for (int num : arr2) {
if (set1.count(num)) {
result.push_back(num);
set1.erase(num); // Avoid duplicates
}
}
return result; // Total: O(n + m)
}
Tanpa hash table: O(n ร m)
| Konsep | Deskripsi |
|---|---|
| Hash Table | Struktur data dengan O(1) average access |
| Fungsi Hash | Division (k mod m), Multiplication, Polynomial |
| Collision | h(kโ) = h(kโ), pasti terjadi (Pigeonhole) |
| Separate Chaining | Linked list di setiap slot |
| Open Addressing | Linear, Quadratic, Double Hashing |
| Load Factor | ฮฑ = n/m, threshold 0.75 |
| Rehashing | Resize table saat ฮฑ tinggi |
Hash table dengan n = 600 elemen dan m = 800 slot.
Jawaban:
Silakan bertanya atau diskusi
Referensi: Cormen Ch.11 | Weiss Ch.5 | Hubbard Ch.8
Struktur Data dan Algoritma
Pertemuan 14
ยฉ 2026 - CC BY 4.0