LRU

####LeetCode 面试题 16.25. LRU 缓存

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

使用单链表

class Node:
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.next = None
class LRUCache:

    def __init__(self, capacity=10):
        '''index存储key对应的Node的Prev信息;head存储实际的链表'''
        self.index = {}
        self.head = Node(-1, -1)
        self.tail = self.head
        self.capacity = capacity

    def put(self, key, val):
        if self.get(key) != -1:
            self.index[key].next.val = val
            return
        node = Node(key, val)
        self.index[node.key] = self.tail
        self.moveToTail(node)
        if self.size() > self.capacity:
            self.evict()

    def get(self, key):
        if key not in self.index:
            return -1

        pre = self.index[key]
        cur = pre.next
        if cur != self.tail:
            pre.next = cur.next
            self.index[cur.next.key] = pre
            self.index[cur.key] = self.tail
            self.moveToTail(cur)
        return cur.val

    def moveToTail(self, node: Node):
        node.next = None
        self.tail.next = node
        self.tail = self.tail.next

    def evict(self):
        head = self.head
        del self.index[head.next.key]
        self.index[head.next.next.key] = self.head
        self.head.next = self.head.next.next
    def size(self):
        return len(self.index)

使用双链表

## Last Frequence Cache


class Node:
    def __init__(self, k, v):
        self.key = k
        self.val = v
        self.prev = None
        self.next = None


class DoubleLinkList:
    def __init__(self):
        self._head = Node(-1, -1)
        self._tail = Node(-1, -1)
        self._head.next = self._tail
        self._tail.prev = self._head
        self.size = 0

    def push(self, node):
        node.prev = self._tail.prev
        node.next = self._tail
        node.prev.next = node
        self._tail.prev = node
        self.size += 1


    def remove(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev
        self.size -= 1

    def head(self):
        return self._head.next

    def __str__(self):
        node = []
        nxt = self._head.next
        for _ in range(self.size):
            node.append(str(nxt.val))
            nxt = nxt.next
        return '->'.join(node)

class LRUCache:
    def __init__(self, capacity=10):
        self.capacity = capacity
        self.data = {}
        self.cache = DoubleLinkList()

    def put(self, key, val):
        if key in self.data:
            node = self.data[key]
            node.val = val
            self.moveToTail(node)
            return node.val

        if len(self.data) >= self.capacity:
            self.remove()
        node = Node(key, val)
        self.data[key] = node
        self.cache.push(node)

    def get(self, key):
        if key not in self.data:
            return -1

        node = self.data[key]
        self.moveToTail(node)
        return node.val

    def moveToTail(self, node):
        self.cache.remove(node)
        self.cache.push(node)

    def remove(self):
        head = self.cache.head()
        del self.data[head.key]
        self.cache.remove(head)
        return head