Целью кэша LRU является поддержка двух операций за O(1)
время: get(key)
и put(key, value)
с дополнительным ограничением на то, что наименее недавно использованные ключи сначала отбрасываются.Обычно ключи являются параметрами вызова функции, а значение является кэшированным выходным сигналом этого вызова.
Независимо от того, как вы подходите к этой проблеме, мы можем согласиться с тем, что вы ДОЛЖНЫ использовать хэш-карту.Чтобы отобразить ключ, уже присутствующий в кеше, в значение O(1)
.
, необходимо иметь хеш-карту. Для того, чтобы справиться с дополнительным ограничением в отношении того, что ключи, которые использовались не так давно, сначала отбрасываются, вы можете использовать LinkedList или ArrayDeque.,Однако, поскольку нам на самом деле не нужен доступ к середине, LinkedList лучше, так как вам не нужно изменять размер.
Редактировать:
Мистер.Тиммерманс обсуждал в своем ответе, почему ArrayDeques
нельзя использовать в LRU cache
из-за необходимости перемещения элементов от середины до конца.С учетом сказанного здесь есть реализация LRU cache
, которая успешно отправляет в leetcode, используя только добавления и дополнения в deque
.Обратите внимание, что Python collections.deque
реализован в виде двусвязного списка, однако мы используем только операции в collections.deque
, которые также являются O(1)
в круговом массиве, поэтому алгоритм остается неизменным независимо.
from collections import deque
class LRUCache:
def __init__(self, capacity: 'int'):
self.capacity = capacity
self.hashmap = {}
self.deque = deque()
def get(self, key: 'int') -> 'int':
res = self.hashmap.get(key, [-1, 0])[0]
if res != -1:
self.put(key, res)
return res
def put(self, key: 'int', value: 'int') -> 'None':
self.add(key, value)
while len(self.hashmap) > self.capacity:
self.remove()
def add(self, key, value):
if key in self.hashmap:
self.hashmap[key][1] += 1
self.hashmap[key][0] = value
else:
self.hashmap[key] = [value, 1]
self.deque.append(key)
def remove(self):
k = self.deque.popleft()
self.hashmap[k][1] -=1
if self.hashmap[k][1] == 0:
del self.hashmap[k]
Я согласен с г-ном Тиммермансом в том, что использование подхода LinkedList предпочтительнее, но я хочу подчеркнуть, что использование ArrayDeque
для построения кэша LRU возможно.
Основная путаница между мной и мистером Тиммермансом заключается в том, как мы интерпретировали способности.Я имел в виду способность кешировать последние N
запросов get / put, в то время как мистер Тиммерманс полагал, что это означает кэширование последних N
уникальных элементов.
Приведенный выше код имеет цикл в put
, который замедляет код - но это просто для того, чтобы код соответствовал кэшированию последних N
уникальных элементов.Если бы у нас был кэш кода последних N
запросов вместо этого, мы могли бы заменить цикл на:
if len(self.deque) > self.capacity: self.remove()
Это сделает его быстрым, если не быстрее, чем вариант связанного списка.
Независимо от того, как интерпретируется maxsize
, вышеуказанный метод по-прежнему работает как кэш LRU
- в первую очередь отбрасываются наименее недавно использованные элементы.
Я просто хочу подчеркнуть, что разработка LRU
кеш таким способом возможен.Источник прямо здесь - попробуйте отправить его на Leetcode!