Created
August 16, 2021 05:47
-
-
Save luisenriquecorona/17f5bd0b89572e52852a9c528abdaa20 to your computer and use it in GitHub Desktop.
LFU Cache.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DoubleLinkedListNode { | |
// Double Linked List Node built specifically for LFU Cache | |
constructor (key, val) { | |
this.key = key | |
this.val = val | |
this.freq = 0 | |
this.next = null | |
this.prev = null | |
} | |
} | |
class DoubleLinkedList { | |
// Double Linked List built specifically for LFU Cache | |
constructor () { | |
this.head = new DoubleLinkedListNode(null, null) | |
this.rear = new DoubleLinkedListNode(null, null) | |
this.head.next = this.rear | |
this.rear.prev = this.head | |
} | |
_positionNode (node) { | |
// Helper function to position a node based on the frequency of the key | |
while (node.prev.key && node.prev.freq > node.freq) { | |
const node1 = node | |
const node2 = node.prev | |
node1.prev = node2.prev | |
node2.next = node1.prev | |
node1.next = node2 | |
node2.prev = node1 | |
} | |
} | |
add (node) { | |
// Adds the given node to the end of the list (before rear) and positions it based on frequency | |
const temp = this.rear.prev | |
temp.next = node | |
node.prev = temp | |
this.rear.prev = node | |
node.next = this.rear | |
this._positionNode(node) | |
} | |
remove (node) { | |
// Removes and returns the given node from the list | |
const tempLast = node.prev | |
const tempNext = node.next | |
node.prev = null | |
node.next = null | |
tempLast.next = tempNext | |
tempNext.prev = tempLast | |
return node | |
} | |
} | |
class LFUCache { | |
// LFU Cache to store a given capacity of data | |
// The Double Linked List is used to store the order of deletion from the cache | |
// The rear.prev holds the most frequently used key and the head.next holds the least used key | |
// When the number of elements reaches the capacity, the least frequently used item is removed before adding the next key | |
constructor (capacity) { | |
this.list = new DoubleLinkedList() | |
this.capacity = capacity | |
this.numKeys = 0 | |
this.hits = 0 | |
this.miss = 0 | |
this.cache = {} | |
} | |
cacheInfo () { | |
// Return the details for the cache instance [hits, misses, capacity, current_size] | |
return `CacheInfo(hits=${this.hits}, misses=${this.miss}, capacity=${this.capacity}, current size=${this.numKeys})` | |
} | |
set (key, value) { | |
// Sets the value for the input key and updates the Double Linked List | |
if (!(key in this.cache)) { | |
if (this.numKeys >= this.capacity) { | |
const keyToDelete = this.list.head.next.key | |
this.list.remove(this.cache[keyToDelete]) | |
delete this.cache[keyToDelete] | |
this.numKeys -= 1 | |
} | |
this.cache[key] = new DoubleLinkedListNode(key, value) | |
this.list.add(this.cache[key]) | |
this.numKeys += 1 | |
} else { | |
const node = this.list.remove(this.cache[key]) | |
node.val = value | |
this.list.add(node) | |
} | |
} | |
get (key) { | |
// Returns the value for the input key and updates the Double Linked List. Returns null if key is not present in cache | |
if (key in this.cache) { | |
this.hits += 1 | |
this.list.add(this.list.remove(this.cache[key])) | |
return this.cache[key].val | |
} | |
this.miss += 1 | |
return null | |
} | |
} | |
function main () { | |
// Example 1 (Small Cache) | |
const cache = new LFUCache(2) | |
cache.set(1, 1) | |
cache.set(2, 2) | |
console.log(cache.get(1)) | |
cache.set(3, 3) | |
console.log(cache.get(2)) // cache miss | |
cache.set(4, 4) | |
console.log(cache.get(1)) // cache miss | |
console.log(cache.get(3)) | |
console.log(cache.get(4)) | |
console.log('Example Cache: ', cache.cacheInfo(), '\n') | |
// Example 2 (Computing Fibonacci Series - 100 terms) | |
function fib (num, cache = null) { | |
if (cache) { | |
const value = cache.get(num) | |
if (value) { return value } | |
} | |
if (num === 1 || num === 2) { return 1 } | |
const result = fib(num - 1, cache) + fib(num - 2, cache) | |
if (cache) { cache.set(num, result) } | |
return result | |
} | |
const fibCache = new LFUCache(100) | |
for (let i = 1; i <= 100; i++) { fib(i, fibCache) } | |
console.log('Fibonacci Series Cache: ', fibCache.cacheInfo(), '\n') | |
} | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment