Open EdwardZZZ opened 3 years ago
class DoubleLinkedListNode {
// Double Linked List Node built specifically for LRU Cache
constructor (key, val) {
this.key = key
this.val = val
this.next = null
this.prev = null
}
}
class DoubleLinkedList {
// Double Linked List built specifically for LRU Cache
constructor () {
this.head = new DoubleLinkedListNode(null, null)
this.rear = new DoubleLinkedListNode(null, null)
this.head.next = this.rear
this.rear.prev = this.head
}
add (node) {
// Adds the given node to the end of the list (before rear)
const temp = this.rear.prev
temp.next = node
node.prev = temp
this.rear.prev = node
node.next = this.rear
}
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 LRUCache {
// LRU Cache to store a given capacity of data
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
}
}
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()
索引双向列表?...
class DoubleLinkedListNode {
constructor(key, val) {
this.key = key
this.val = val
this.next = null
this.prev = null
}
}
class DoubleLinkedList extends Array {
constructor() {
this.head = new DoubleLinkedListNode(null, null)
this.rear = new DoubleLinkedListNode(null, null)
this.head.next = this.rear
this.rear.prev = this.head
}
add(node) {
const temp = this.rear.prev
temp.next = node
node.prev = temp
this.rear.prev = node
node.next = this.rear
this[this.length - 1] = node;
}
remove(node) {
const index = this.findIndex(node);
if (index > -1) this.splice(index, 1);
const tempLast = node.prev
const tempNext = node.next
node.prev = null
node.next = null
tempLast.next = tempNext
tempNext.prev = tempLast
return node
}
}