###### tags: `Leetcode` `medium` `design` `hash` `list` `double-linked list` `python` `Top 100 Liked Questions` # 146. LRU Cache ## [題目連結:] https://leetcode.com/problems/lru-cache/ ## 題目: Design a data structure that follows the constraints of a **Least Recently Used (LRU) cache**. Implement the ```LRUCache``` class: * ```LRUCache(int capacity)``` Initialize the LRU cache with **positive** size ```capacity```. * ```int get(int key)``` Return the value of the ```key``` if the key exists, otherwise return ```-1```. * ```void put(int key, int value)``` Update the value of the ```key``` if the ```key``` exists. Otherwise, add the ```key-value``` pair to the cache. If the number of keys exceeds the ```capacity``` from this operation, **evict** the least recently used key. The functions ```get``` and ```put``` must each run in ```O(1)``` average time complexity. **Example 1:** ``` Input ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] Output [null, null, null, 1, null, -1, null, -1, 3, 4] Explanation LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // cache is {1=1} lRUCache.put(2, 2); // cache is {1=1, 2=2} lRUCache.get(1); // return 1 lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3} lRUCache.get(2); // returns -1 (not found) lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3} lRUCache.get(1); // return -1 (not found) lRUCache.get(3); // return 3 lRUCache.get(4); // return 4 ``` ## 解題想法: * 題目要求實作LRU cache * get、put function: O(1) * 初始給cache大小 * put時候,若還有空間,直接放入即可 * 若沒空間,則將**最近最少使用的**將其替換 ## Python_Sol1: collections.OrderedDict() * OrderedDict(): * 每次加入新(key,val),會放到最尾 * Python 3.1以上支援,比一般defaultdict()多了 * **popitem(last=True)** * returns and removes a (key, value) pair * last=False,表示pop頭 * **move_to_end(key, last=True)** * Move an existing key to either end of an ordered dictionary. * last=False,表示加到頭 * 想法: * get(key): * **case1:** 若key存在於dic * 並將(key,val) 移到尾: * move_to_end(key,True) * return 其val * **case2:** 若key不存在dic * return -1 * put(key,val): 將最近用到的移到dic尾 * **case1:** 若key存在於dic,則更改dic[key]之val,並將(key,val) move_to_end * **case2:** 若key不存在: * 若size滿了: * 則需先移除最近沒使用的頭popitem(last=False) * 再創建新的(key,val) * 因為是OrderedDict,新創的會加到尾,如我們所意 ``` python= from collections import OrderedDict class LRUCache(object): def __init__(self, capacity:int) -> None: self.dic=OrderedDict() self.capacity=capacity self.size=0 #count item in cache def get(self,key:int)->int: if key in self.dic: self.dic.move_to_end(key,last=True) return self.dic[key] else: return -1 def put(self,key:int, value:int)->None: if key in self.dic: self.dic[key]=value self.dic.move_to_end(key,last=True) else: if self.size<self.capacity: #enough self.dic[key]=value self.size+=1 else: # not enough ,need to pop self.dic.popitem(last=False) #last=True self.dic[key]=value #一進一出 size不變 if __name__=='__main__': result = LRUCache(2) result.put(1,1) result.put(2,2) print(result.dic) #OrderedDict([(1, 1), (2, 2)]) result.put(3,3) print(result.dic) #OrderedDict([(2, 2), (3, 3)]) result.get(2) result.put(4,4) print(result.dic) #OrderedDict([(2, 2), (4, 4)]) ``` ## Python_Sol2: Double-linked list + hashtable * 正規作法....但 * 因為沒有double-linked list套件..只能自己刻.. * 但總不可能面試時跟考官說..阿等等 我先刻個doublelist來用 (´・_・`) ``` python= #用雙向list + hashtable class ListNode(): def __init__(self,key,val,pre=None,next=None): self.key=key self.val=val self.pre=pre self.next=next class DoubleLiskedList(): def __init__(self,capacity:int): self.capacity=capacity #size of doublelinklist self.size=0 #actual total self.head=None #pointer to list head self.tail=None #pointer to list tail #time: O(1) def append(self,node:ListNode)->None: if self.size==self.capacity: print("full") return self.size+=1 #第一個加入 if self.head==None: self.head=node self.tail=node else: #新node加到尾巴 self.tail.next=node node.pre=self.tail #把tail指到最尾 self.tail=node #time: O(1) def delete(self,node:ListNode)->ListNode: #刪除時,考量 #1.)是否==head #2.)是否==tail #3.)在中間 if self.size==0: print("already empty") return self.size-=1 #1.)是否==head if node==self.head: if node.next: #將右邊的pre指None node.next.pre=None #將head向右移 self.head=node.next #2.)是否==tail elif node==self.tail: #若左邊還有node if node.pre: node.pre.next=None #將tail向左移 self.tail=node.pre #3.)在中間 else: #左右分別跳過該node node.pre.next=node.next node.next.pre=node.pre return node class LRUCache(object): def __init__(self,capacity:int): self.capacity=capacity self.dic={} #key: key , val:ListNode self.cacheList=DoubleLiskedList(capacity) def get(self,key:int)->int: if key in self.dic: node=self.dic[key] #刪掉該node並加到最尾 self.cacheList.delete(node) self.cacheList.append(node) return node.val else: return -1 def put(self,key:int, value:int)->None: if key in self.dic: node=self.dic[key] #更新其value node.val=value #刪掉該node並加到最尾 self.cacheList.delete(node) self.cacheList.append(node) else: #滿了要替換 替換掉頭 if self.capacity==self.cacheList.size: #把doublelist中head刪掉並回傳 head_node=self.cacheList.delete(self.cacheList.head) #刪掉dic中期資訊 del self.dic[head_node.key] #創建新node new_node=ListNode(key,value) #加入新的到dic self.dic[key]=new_node #並加入到doubleList self.cacheList.append(new_node) if __name__=='__main__': result=LRUCache(2) result.put(1,100) result.put(2,200) for key in result.dic: print(key,result.dic[key].val) ''' 1 100 2 200 ''' res1=result.get(2) print(res1) #200 result.put(4,400) for key in result.dic: print(key,result.dic[key].val) ''' 2 200 4 400 ''' ```