✍️ Note
Some codes are sourced from Holczer Balazs’s Udemy Course (https://www.udemy.com/course/algorithms-and-data-structures/learn/lecture/9779302#content). This post is for personal notes where I summarize the original contents to grasp the key concepts
Least Recently Used Cache
We want to access the recently used items
- For example URLs very fast so in O(1) time complexity and discard least recently used ones
- Naive Approach: Use a single hash table and we can achieve put() and get() operations in O(1) but memory complexity is not favorable
- Splay trees are working fine but again we store all the items in a tree-like structure
- LRU caches use (usually) doubly linked lists to achieve this goal + we have to use hash tables as well to boost linked list!!
https://www.udemy.com/course/algorithms-and-data-structures/learn/lecture/9779306#content
public class Node {
public var prev: Node?
public var next: Node?
public var id: String
public var data: String
public init(prev: Node? = nil, next: Node? = nil, id: String, data: String) {
self.prev = prev
self.next = next
self.id = id
self.data = data
}
}
public class LRUCacheDoublyLinkedList {
private var size: Int = 0
let capacity: Int
private var head: Node?
private var tail: Node?
private var dict: [String: Node] = [:]
public init(capacity: Int) {
self.capacity = capacity
}
//Update the node to be the head
private func update(_ node: Node) {
let prev = node.prev
let next = node.next
//It is a middle node in the list
if prev != nil {
prev?.next = next
}
else {
//This case It is first node(head)
head = next
}
//If it is not last node
if next != nil {
next?.prev = prev
}
else {
//It is the last node (tail)
tail = prev
}
add(node)
}
//get item with ID and move to the head because It used
public func get(_ id: String) -> Node? {
guard let node = dict[id] else {
return nil
}
update(node)
return node
}
//Always add node at Head
private func add(_ node: Node) {
node.next = head
if head != nil {
head?.prev = node
}
//Set new head node
head = node
//If there is 1 node in the list, it is the head as well as the tail
if tail == nil {
tail = node
}
dict[node.id] = node
}
private func removeTail() {
let tailId = tail?.id
var cachedTail: Node?
if let tailId = tail?.id, let lastNode = dict[tailId] {
cachedTail = lastNode
}
tail = tail?.prev
if tail != nil {
tail?.next = nil
}
if let tailId {
dict.removeValue(forKey: tailId)
}
cachedTail = nil
}
public func put(id: String, data: String) {
if let node = dict[id] {
node.data = data
update(node)
return
}
//the data is not present in the cache so insert
let newNode = Node(id: id, data: data)
//Check cache size
if size < capacity {
size += 1
//insert into the cache and set it to be the head node
add(newNode)
}
else {
//cache is full, remove the last item and insert new one
removeTail()
add(newNode)
}
}
public func printAll() {
var pointNode = head
while pointNode != nil {
if let data = pointNode?.data {
print("\(data)", terminator: pointNode?.next != nil ? " -> " : "")
}
pointNode = pointNode?.next
}
print("\n")
}
}
댓글 남기기