• 数据结构和算法-LRU


    LRU, 内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据

    缓存使用策略有三种

    • FIFO(先进先出队列)
    • LFU - Least frequently Used (最少使用)
      如果一段数据在最近一段时间被访问的次数很少, 那么在将来被访问的次数也很少
    • LRU - Least Recently Used(最近最少使用)
      如果一段数据在最近的一段时间没有被访问到, 那么之后被访问到的概率很小

    方案一: O(n)

    维护一个有序单链表. 尾部是最近使用的, 头部是最早使用的. 当有一个新数据被访问时, 遍历该链表, 有以下情况

    1. 如果已经被缓存在链表中, 得到这个节点, 删除后移动到最尾部
    2. 如果没有被缓存
      • 如果链表未满, 直接加入尾部
      • 如果链表满了, 删除头部, 把新节点加入尾部

    如果直接使用单链表实现的话, 查找操作时间复杂度是O(n), 所以通常还会再借助散列表来提高查找速度. 但是在空间已满的时候需要删除最早使用的数据, 所以还需要保证使用顺序

    方案二: O(1)

    实现有序的散列表: 借助散列表和双向链表实现, 散列表用来快速定位, 双向链表用来存储数据

    实现

    使用OrderDict实现

    # coding:utf-8
    
    from collections import OrderedDict
    
    
    class LRUCache(object):
        """
        借助OrderedDict的有序性实现, 内部使用了双向链表
        """
    
        def __init__(self, max_length: int = 5):
            self.max_length = max_length
            self.o_dict = OrderedDict()
    
        def get(self, key):
            """
            如果找到的话移动到尾部
            :param key:
            :return:
            """
            value = self.o_dict.get(key)
            if value:
                self.o_dict.move_to_end(key)
            return value
    
        def put(self, key, value):
            if key in self.o_dict:
                self.o_dict.move_to_end(key)
            else:
                if len(self.o_dict) >= self.max_length:
                    # 弹出最先插入的元素
                    self.o_dict.popitem(last=False)
            self.o_dict[key] = value
    
    
    if __name__ == "__main__":
        lru = LRUCache(max_length=3)
        lru.put(1, "a")
        lru.put(2, "b")
        lru.put(3, "c")
        assert lru.o_dict == OrderedDict([(1, 'a'), (2, 'b'), (3, 'c')])
        lru.get(2)
        assert lru.o_dict == OrderedDict([(1, 'a'), (3, 'c'), (2, 'b')])
        lru.put(4, "d")
        assert lru.o_dict == OrderedDict([(3, 'c'), (2, 'b'), (4, "d")])
    
    
    

    使用dict和list实现

    # coding:utf-8
    
    from collections import deque
    
    
    class LRUCache(object):
        def __init__(self, max_length: int = 5):
            self.max_length = max_length
            self.cache = dict()
            self.keys = deque()
    
        def get(self, key):
            if key in self.cache:
                value = self.cache[key]
                self.keys.remove(key)
                self.keys.append(key)
            else:
                value = None
            return value
    
        def put(self, key, value):
            if key in self.cache:
                self.keys.remove(key)
                self.keys.append(key)
            else:
                if len(self.keys) >= self.max_length:
                    self.keys.popleft()
                    self.keys.append(key)
                else:
                    self.keys.append(key)
            self.cache[key] = value
    
    
    if __name__ == "__main__":
        lru = LRUCache(max_length=3)
        lru.put(1, "a")
        lru.put(2, "b")
        lru.put(3, "c")
        assert lru.keys == deque([1, 2, 3])
        lru.get(2)
        assert lru.keys == deque([1, 3, 2])
        lru.put(4, "d")
        assert lru.keys == deque([3, 2, 4])
    
    

    lru_cache

    可以使用python3.7中自带的lru缓存模块

    from functools import lru_cache
    
    
    @lru_cache(maxsize=32)
    def fibs(n: int):
        if n == 0:
            return 0
        if n == 1:
            return 1
        return fibs(n-1) + fibs(n-2)
    
    
    if __name__ == '__main__':
        print(fibs(10))
    
    

    应用

    • Redis的LRU策略
    • Java的LinkedHashMap
      使用散列表和双向链表实现

    总结

    • 数组利用索引可以快速定位, 但是缺陷是需要内存连续
    • 链表优势是内存可以不连续, 但是查找慢
    • 散列表和链表/跳表混合使用是为了结合数组和链表的优势

    资料

    • 数据结构和算法之美-王争
  • 相关阅读:
    Spring核心概念
    机器学习第二次作业
    机器学习第一次作业
    软工实践个人总结
    第04组 Beta版本演示
    第04组 Beta冲刺(5/5)
    第04组 Beta冲刺(4/5)
    第04组 Beta冲刺(3/5)
    第04组 Beta冲刺(2/5)
    第04组 Beta冲刺(1/5)
  • 原文地址:https://www.cnblogs.com/zlone/p/11012097.html
Copyright © 2020-2023  润新知