[Python 으로 푸는 Leetcode]146. LRU Cache

느린 개발자·2021년 1월 2일
0

Coding Test

목록 보기
18/21

📌Problem

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.

Follow up:
Could you do get and put in O(1) 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

Constraints:

  • 1 <= capacity <= 3000
  • 0 <= key <= 3000
  • 00 <= value <= 10410^4

At most 31043 * 10^4 calls will be made to get and put.

leetcode 에서 풀기


💡LRU(Least Recently Used) Cache

📃 Cache

Cache자주 사용하는 데이터나 값을 미리 복사해 놓는 임시장소 를 가리킨다.
아래와 같은 저장공간 계층 구조에서 확인 할 수 있듯이, cache저장공간이 작고 비용이 비싼 대신 빠른 성능을 제공한다.

📃 Cache Metrics

cache 의 성능을 측정할 때 Hit latencyMiss latency 가 중요한 요인으로 꼽힌다.

  • Hit latency : Cache hit(CPU에서 요청한 데이터가 캐시에 존재하는 경우) 가 발생해 캐싱된 데이터를 가져올 때 소요되는 시간.

  • Miss latency : Cache miss(CPU에서 요청한 데이터가 캐시에 존재하지 않는 경우) 가 발생해 상위 캐시 혹은 메모리에서 데이터를 가져올 때 소요되는 시간.

📃 LRU Algorithm

우리가 관심을 가져야 할 것은 한정된 자원속에서 cache hit 를 높이고 cache miss 를 줄이는 방법이다. 그 중 LRU algorithm가장 최근에 사용된 적이 없는 데이터는 앞으로도 사용될 확률이 적을 거라는 판단으로 추가되는 새로운 데이터와 교체하는 알고리즘이다. 아래 그림과 같이 Head가까울 수록 가장 최근에 사용된 데이터 이며, Tail가까울수록 최근에 사용되지 않은 데이터이다.


💡 OrderedDict

파이썬 3.6 이전의 dict 는 데이터를 삽입된 순서대로 데이터를 얻을수가 없었다. 대신 collections 모듈의 OrderedDict를 통해 데이터의 순서를 보장받을수 있다. (파이썬 3.6 부터는 기본 dictOrderedDict 와 동일하게 동작하기 때문에 사용할일은 없을지라도 하위 호환성 보장 측면에서 사용하는것을 권장한다.)

📃 popitem(last=True)

it returns and removes a (key,value) pair

  • last=True : The pairs(key-value) are returned in LIFO(Last In First Out)
  • last=False : The pairs(key-value) are returned in FIFO(First In First Out)

📃 move_to_end(key,last=True)

Move an existing key to either end of an ordered dictionary

  • last=True : The item is moved to the right
  • last=False : The item is moved to the left

📝Solution

from collections import OrderedDict
class LRUCache:
    
    def __init__(self, capacity: int):
        self.__capacity=capacity
        self.__cache=OrderedDict()
        

    def get(self, key: int) -> int:
        value=self.__cache.get(key,-1)
        if value !=-1: # If key exists
            self.__cache.move_to_end(key,last=True) # Update time(Move to header)
        
        return value
        
    def put(self, key: int, value: int) -> None:
        if self.__cache.get(key,False): # If key exists
            self.__cache.move_to_end(key,last=True) # Update time (Move to header)
        elif len(self.__cache)==self.__capacity: # If cache exceeds capacity
            self.__cache.popitem(last=False) # FIFO
                        
        self.__cache[key]=value # Update key-value 
            
                        
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

📚Reference

The figure for header ## Cache
Header ## Cache Metrics

profile
남들보단 느리지만, 끝을 향해

0개의 댓글