LRU Cache — Struktur Data

LRU Cache (Least Recently Used) adalah cache berkapasitas tetap yang membuang item paling lama tidak digunakan ketika penuh. Konsep ini dipakai di mana-mana — b

LRU Cache (Least Recently Used) adalah cache berkapasitas tetap yang membuang item paling lama tidak digunakan ketika penuh.

Konsep ini dipakai di mana-mana — browser cache, Memcached, Redis maxmemory-policy allkeys-lru, database query cache, sampai halaman virtual memory di OS.

Tujuan

Punya dua operasi, keduanya O(1):

Operasi Arti
get(key) Ambil value kalau ada; tandai key ini sebagai "baru digunakan"
put(key, value) Simpan; kalau kapasitas penuh, buang item paling lama tidak digunakan

Mengapa harus kombinasi dua struktur?

Satu struktur saja tidak cukup:

Solusinya: Hash Map + Doubly Linked List.

Saat get(key): lookup lewat map → pindahkan node ke kepala list. Saat put(key, value) dan cache penuh: hapus node ekor, hapus entry-nya dari map, pasang yang baru di kepala.

Karena node linked list menyimpan pointer prev dan next, memindahkan dan menghapus bisa O(1) asal kita punya pointer langsung ke node-nya — dan itu yang diberikan oleh hash map.

Implementasi dengan Map JavaScript

JavaScript punya kelebihan praktis: Map mempertahankan urutan insertion. Kita bisa pakai itu sebagai pengganti doubly linked list — hapus-lalu-set memindahkan key ke posisi "paling baru".

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.map = new Map();
  }

  get(key) {
    if (!this.map.has(key)) return -1;
    const value = this.map.get(key);
    // Refresh posisi — hapus lalu set agar jadi paling baru
    this.map.delete(key);
    this.map.set(key, value);
    return value;
  }

  put(key, value) {
    if (this.map.has(key)) {
      this.map.delete(key); // buang posisi lama
    } else if (this.map.size >= this.capacity) {
      // Buang yang paling lama — iterator pertama Map
      const oldestKey = this.map.keys().next().value;
      this.map.delete(oldestKey);
    }
    this.map.set(key, value);
  }
}

Contoh penggunaan:

const cache = new LRUCache(2);
cache.put(1, "A");
cache.put(2, "B");
cache.get(1);      // "A" — sekarang 1 paling baru
cache.put(3, "C"); // kapasitas penuh → buang 2 (paling lama)
cache.get(2);      // -1 (sudah dievict)

Kompleksitas

Operasi Kompleksitas
get(key) O(1)
put(key, value) O(1)

Dengan implementasi "manual" pakai HashMap + DoublyLinkedList, kompleksitas tetap O(1) tanpa bergantung pada perilaku insertion-order Map.

Kapan pakai LRU Cache?

Trivia interview: LRU Cache adalah LeetCode 146 — salah satu soal yang paling sering keluar untuk posisi mid/senior. Intinya: tunjukkan kamu paham kenapa satu struktur tidak cukup.

🎭 Analogi sehari-hari: LRU = lemari baju yang penuh. Mau tambah baju baru? Buang baju yang paling lama gak dipakai (di tumpukan paling bawah). Setiap pakai baju → taruh paling atas. Yang sering dipakai naik terus, yang dilupakan turun terus sampai dibuang.

💡 Trik JS Map: Map di JS pertahankan insertion order. Hapus-lalu-set bikin entry pindah ke "paling baru". Inilah kenapa contoh ini bisa O(1) tanpa linked list manual — JS Map kebetulan sudah punya semantik yang dibutuhkan.

⚠️ Jebakan umum:

🎯 LRU vs LFU vs FIFO cache:

TL;DR: LRU = cache yang buang "paling lama gak dipakai". O(1) dengan HashMap + DoublyLinkedList (atau JS Map). LeetCode 146 wajib jago.

Yang akan kamu pelajari