LRU 缓存淘汰策略
LRU(Least Recent Used)缓存淘汰策略
LRU 算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
实现细节
核心思想使用 map 结构做到 save 和 get key的时间都是 O(1),配合双向链表完成 O(1) 时间将节点放置在缓存头部。
编码细节:
- save(key, value)
- 首先在 Map 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。
- 如果不存在,需要构造新的节点,并且尝试把节点塞到队头,如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 Map 中移除 Key。
- get(key)
- 通过 Map 找到 LRU 链表节点,因为根据LRU 原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值。
Python 实现
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Node(object):
"""节点包含前后指针和键值"""
def __init__(self, key, value, pre=None, post=None):
self.key = key
self.value = value
self.pre = pre
self.post = post
def __repr__(self):
return 'Node({},{})'.format(self.key, self.value)
__str__ = __repr__
class LRUCache(object):
def __init__(self, capacity=10):
"""容量大小,初始化头尾节点"""
self.size = 0
self.capacity = capacity
self.head = Node(-1, -1)
self.tail = Node(-1, -1)
self.head.post = self.tail
self.tail.pre = self.head
self.key_map = dict()
def set(self, k, v):
node = self.key_map.get(k)
if node is not None:
self.key_map[k] = v
self._move_node(node)
else:
node = Node(k, v)
self._add_node(node)
self.key_map[k] = node
self.size += 1
if self.size >= self.capacity:
node = self._pop_node()
self.key_map.pop(node.key)
self.size -= 1
def get(self, k):
"""获取操作"""
node = self.key_map.get(k)
if node is not None:
self._move_node(node)
return node.value
else:
print('miss key')
return -1
# raise KeyError('key not exist')
def _add_node(self, node):
"""增加node至head后"""
node.pre = self.head
node.post = self.head.post
self.head.post.pre = node
self.head.post = node
def _remove_node(self, node):
"""删除node"""
node.pre.post = node.post
node.post.pre = node.pre
def _move_node(self, node):
"""移动至头部"""
self._remove_node(node)
self._add_node(node)
def _pop_node(self):
"""弹出尾节点"""
node = self.tail.pre
self._remove_node(node)
return node
def print_node(self):
node = self.head
while node.post != self.tail:
yield node.post
node = node.post
if __name__ == '__main__':
import random
cache = LRUCache()
for i in range(20):
k = random.randint(0, 5)
if k % 2 == 0:
cache.set(i, i)
print('set {}'.format(i))
else:
v = cache.get(k)
print('get {}, value {}'.format(k, v))
print('->'.join(str(i) for i in cache.print_node()))