time O(N) space O(N)
class LRUCache:
# data structure queue, store key and value
def __init__(self, capacity: int):
self.capacity = capacity
self.queue = deque()
# find index from queue based on key; else return -1
def _find(self, key):
for i in range(len(self.queue)): # time O(N)
tup = self.queue[i]
if tup[0] == key:
return i
return -1
def get(self, key: int) -> int:
if self._find(key) == -1:
return -1
# remove the k,v and append to tail
index = self._find(key)
k,v = self.queue[index]
del self.queue[index] # O(N)
self.queue.append((k,v))
return v
def put(self, key: int, value: int) -> None:
# check if exist: 1. exist 1.a remove ; append to tail
#2. not exist: 2.a exceed limit-> remove head value by popleft() then append to tail;
#2.b not exceed: append to tail
idx = self._find(key)
if idx == -1:
if len(self.queue) == self.capacity:
self.queue.popleft()
else:
del self.queue[idx]
self.queue.append((key,value))
improvement: use dictionary to store key and value ; use queue to track the order of key with LRU feature;
from collections import deque
# time O(N) space O(N)
class LRUCache:
# data structure queue, store key and value
def __init__(self, capacity: int):
self.capacity = capacity
self.queue = deque()
self.valueMap = dict()
# find index from queue based on key; else return -1
def get(self, key: int) -> int:
if key not in self.valueMap:
return -1
else:
self.queue.remove(key)
self.queue.append(key)
return self.valueMap[key]
def put(self, key: int, value: int) -> None:
# check if exist: 1. exist 1.a remove ; append to tail
#2. not exist: 2.a exceed limit-> remove head value by popleft() then append to tail;
#2.b not exceed: append to tail
if key not in self.valueMap:
if len(self.queue) == self.capacity:
temp = self.queue.popleft()
del self.valueMap[temp]
else:
self.queue.remove(key)
self.queue.append(key)
self.valueMap[key]=value
solution 3: double linkedlist + dictionary to store reference of node, where each node store key, val, prev, head
#time O(1) space O(N)
class LRUCache:
# data structure: linkedlist, each node store k,v, ; use dictionay to store node reference
class Node:
def __init__(self,k=-1,v=-1):
self.k = k
self.v = v
self.prev = None
self.nxt = None
def __init__(self, capacity: int):
self.size = capacity
self.cnt = 0
self.d = {} # {key: Node}
self.head = self.Node()
self.tail = self.Node()
self.head.nxt = self.tail
self.tail.prev = self.head
# tc O(1) sc O(1) 1.remove entry in hashmap 2.remove node in DLList
def _remove(self,node)-> None:
del self.d[node.k]
node.prev.nxt = node.nxt
node.nxt.prev = node.prev
#self.cnt -= 1
# tc O(1) sc O(1) check if key in hashmap : yes -- 1. remove current node 2. put new key,val pair Node; no --- return -1
def get(self, key: int) -> int:
if key in self.d:
node = self.d[key]
val = node.v # cache value
self._remove(node)
self.put(key,val)
return val
return -1
#tc O(1) sc O(N) 1. check if key in hashmap yes: remove existing node 2. add new node 3. check if current DLList exceed capacity, remove last node in the tail
def put(self, key: int, value: int) -> None:
if key in self.d:# process updated value for same key
node = self.d[key]
self._remove(node)
new_node = self.Node(key,value)
new_node.nxt = self.head.nxt
self.head.nxt = new_node
new_node.nxt.prev = new_node
new_node.prev = self.head
self.d[key] = new_node
#self.cnt += 1
if len(self.d) > self.size: # self.cnt > self.size
last = self.tail.prev
self._remove(last)
# case1: remove node next to head; case2:remove next to rear; case3: remove bewteen normal ===> catually make no diff
def _remove(self,node):
node.prev.next = node.next
node.next.prev = node.prev
def _insert_to_tail(self,node):
self.rear.prev.next = node
node.prev = self.rear.prev
node.next = self.rear
self.rear.prev = node
lazy solution: use OrderedDict from python collections lib
from collections import OrderedDict
class LRUCache:
# data structure: OrderedDict
def __init__(self,capacity):
self.cap = capacity
self.cache = OrderedDict()
def get(self,key):
if key not in self.cache:
return -1
v = self.cache.pop(key)
self.cache[key] = v
return v
def put(self,key,value):
if key in self.cache:
self.cache.pop(key)
self.cache[key] = value
else:
self.cache[key] = value
if len(self.cache)> self.cap:
self.cache.popitem(last=False)