LeetCode146:LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

LeetCode:链接

双向链表 + hash table。

缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的 CPU 缓存、数据库缓存、浏览器缓存等等。

缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略 FIFO(First In,First Out)、最少使用策略 LFU(Least Frequently Used)、最近最少使用策略 LRU(Least Recently Used)。

为了能够快速删除最久没有访问的数据项和插入最新的数据项,我们使用双向链表连接Cache中的数据项,并且保证链表维持数据项从最近访问到最旧访问的顺序每次数据项被查询到时,都将此数据项移动到链表头部(O(1)的时间复杂度)。这样,在进行过多次查找操作后,最近被使用过的内容就向链表的头移动,而没有被使用的内容就向链表的后面移动。当需要替换时,链表最后的位置就是最近最少被使用的数据项,我们只需要将最新的数据项放在链表头部,当Cache满时,淘汰链表最后的位置就是了

注: 对于双向链表的使用,基于两个考虑。首先是Cache中块的命中可能是随机的,和Load进来的顺序无关。其次,双向链表插入、删除很快,可以灵活的调整相互间的次序,时间复杂度为O(1)。

查找一个链表中元素的时间复杂度是O(n),每次命中的时候,我们就需要花费O(n)的时间来进行查找,如果不添加其他的数据结构,这个就是我们能实现的最高效率了。目前看来,整个算法的瓶颈就是在查找这里了,怎么样才能提高查找的效率呢?Hash表,对,就是它,数据结构中之所以有它,就是因为它的查找时间复杂度是O(1)。

梳理一下思路:对于Cache的每个数据块,我们设计一个数据结构来储存Cache块的内容,并实现一个双向链表,其中属性next和prev是双向链表的两个指针,key用于存储对象的键值,value用于存储cache块对象本身。

class Node(object):
    def __init__(self, key, value):
        self.key = key 
        self.value = value
        self.prev = None
        self.next = None
 
class DoubleLinkedList(object):
    def __init__(self):
        self.head = None
        self.tail = None
 
    def removeLast(self):
        self.remove(self.tail)
 
    def remove(self, node):
        # 说明双向链表为空或者只有一个结点 
        if self.head == self.tail:
            self.head = self.tail = None
            return 
        elif node == self.head:
            node.next.prev = None
            self.head = node.next
            return
        elif node == self.tail:
            node.prev.next = None
            self.tail = node.prev
            return
        node.next.prev = node.prev
        node.prev.next = node.next
 
    def addFirst(self, node):
        # 如果链表为空时添加新结点的情况
        if not self.head:
            self.head = self.tail = node
            node.next = node.prev = None
            return
        node.next = self.head
        self.head.prev = node
        # 重新定义新的头结点
        self.head = node
        node.prev = None
 
class LRUCache(object):
    def __init__(self, capacity):
        """
        :type capacity: int
        """
        self.capacity = capacity
        self.size = 0
        self.cache = DoubleLinkedList()
        self.P = dict()        
 
    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if key in self.P:
            # 当查询一次之后就要把当前的节点删掉 插入到头结点
            self.cache.remove(self.P[key])
            self.cache.addFirst(self.P[key])
            return self.P[key].value
        else:
            return -1
        
    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: void
        """
        if key in self.P:
            # 如果在字典里 先删除原来的位置
            self.cache.remove(self.P[key])
            # 将字典中的value值替换成新值
            self.P[key].value = value
            # 再添加到头结点
            self.cache.addFirst(self.P[key])
        else:
            node = Node(key, value)
            # 字典储存的是结点值
            self.P[key] = node
            self.cache.addFirst(node)
            self.size += 1
            if self.size > self.capacity:
                self.size -= 1
                '''必须先删除字典的尾结点的值,然后再从双向链表删除!!'''
                del self.P[self.cache.tail.key]
                '''如果先删除双向链表,字典中的尾结点就不再是应该删除的节点了''' 
                self.cache.removeLast()
        
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值