LRU, 内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据
缓存使用策略有三种
1、FIFO(先进先出队列)
2、LFU - Least frequently Used (最少使用)
如果一段数据在最近一段时间被访问的次数很少, 那么在将来被访问的次数也很少
3、LRU - Least Recently Used(最近最少使用)
如果一段数据在最近的一段时间没有被访问到, 那么之后被访问到的概率很小
方案一: O(n)
维护一个有序单链表. 尾部是最近使用的, 头部是最早使用的. 当有一个新数据被访问时, 遍历该链表, 有以下情况
如果已经被缓存在链表中, 得到这个节点, 删除后移动到最尾部
如果没有被缓存
如果链表未满, 直接加入尾部
如果链表满了, 删除头部, 把新节点加入尾部
如果直接使用单链表实现的话, 查找操作时间复杂度是O(n), 所以通常还会再借助散列表来提高查找速度. 但是在空间已满的时候需要删除最早使用的数据, 所以还需要保证使用顺序
方案二: O(1)
实现有序的散列表: 借助散列表和双向链表实现, 散列表用来快速定位, 双向链表用来存储数据
实现
使用OrderDict实现
1# coding:utf-823from collections import OrderedDict456class LRUCache(object):7 """
8 借助OrderedDict的有序性实现, 内部使用了双向链表
9 """1011 def __init__(self, max_length: int = 5):12 self.max_length = max_length13 self.o_dict = OrderedDict()1415 def get(self, key):16 """
17 如果找到的话移动到尾部
18 :param key:
19 :return:
20 """21 value = self.o_dict.get(key)22 if value:23 self.o_dict.move_to_end(key)24 return value2526 def put(self, key, value):27 if key in self.o_dict:28 self.o_dict.move_to_end(key)29 else:30 if len(self.o_dict) >= self.max_length:31 # 弹出最先插入的元素32 self.o_dict.popitem(last=False)33 self.o_dict[key] = value343536if __name__ == "__main__":37 lru = LRUCache(max_length=3)38 lru.put(1, "a")39 lru.put(2, "b")40 lru.put(3, "c")41 assert lru.o_dict == OrderedDict([(1, 'a'), (2, 'b'), (3, 'c')])42 lru.get(2)43 assert lru.o_dict == OrderedDict([(1, 'a'), (3, 'c'), (2, 'b')])44 lru.put(4, "d")45 assert lru.o_dict == OrderedDict([(3, 'c'), (2, 'b'), (4, "d")])
使用dict和list实现
1# coding:utf-823from collections import deque456class LRUCache(object):7 def __init__(self, max_length: int = 5):8 self.max_length = max_length9 self.cache = dict()10 self.keys = deque()1112 def get(self, key):13 if key in self.cache:14 value = self.cache[key]15 self.keys.remove(key)16 self.keys.append(key)17 else:18 value = None19 return value2021 def put(self, key, value):22 if key in self.cache:23 self.keys.remove(key)24 self.keys.append(key)25 else:26 if len(self.keys) >= self.max_length:27 self.keys.popleft()28 self.keys.append(key)29 else:30 self.keys.append(key)31 self.cache[key] = value323334if __name__ == "__main__":35 lru = LRUCache(max_length=3)36 lru.put(1, "a")37 lru.put(2, "b")38 lru.put(3, "c")39 assert lru.keys == deque([1, 2, 3])40 lru.get(2)41 assert lru.keys == deque([1, 3, 2])42 lru.put(4, "d")43 assert lru.keys == deque([3, 2, 4])
lru_cache
可以使用python3.7中自带的lru缓存模块
1from functools import lru_cache234@lru_cache(maxsize=32)5def fibs(n: int):6 if n == 0:7 return 08 if n == 1:9 return 110 return fibs(n-1) + fibs(n-2)111213if __name__ == '__main__':14 print(fibs(10))
应用
Redis的LRU策略
Java的LinkedHashMap
使用散列表和双向链表实现
总结
数组利用索引可以快速定位, 但是缺陷是需要内存连续
链表优势是内存可以不连续, 但是查找慢
散列表和链表/跳表混合使用是为了结合数组和链表的优势
资料
数据结构和算法之美-王争
【无门槛免费领】
535G超强程序员编程
0基础从入门到精通自学视频教程!
万水千山总是情,点个 “ 在看” 行不行