LRU Cache的原理和python的实现
LRU的原理
LRU(Least Recently Used)即最近最少使用。 操作系统中一种内存管理的页面置换算法,主要用于找出内存中较久时间没有使用的内存块,将其移出内存从而为新数据提供空间。此算法的前提是认为:当一个数据被访问的越频繁,则这个数据在未来被访问的概率就越高。
LRU算法其实就是按照使用顺序将元素倒叙排列,在有限的存储空间中,若空间满时,删除最后的元素(即最久没有使用的元素)。若想实现高效实现这个算法,使get和set都能O(1)的效率,需要一个有序的字典。
LRU的Python实现(库函数版)
首先实现一个双向链表, 没啥好说的,简单实现一下:
class Node:
def __init__(self, data, _pre=None, _next=None):
self.data = data
self._pre = _pre
self._next = _next
def __str__(self):
return str(self.data)
class DoublyLink:
def __init__(self):
self.tail = None
self.head = None
self.size = 0
def insert(self, data):
if isinstance(data, Node):
tmp_node = data
else:
tmp_node = Node(data)
if self.size == 0:
self.tail = tmp_node
self.head = self.tail
else:
self.head._pre = tmp_node
tmp_node._next = self.head
self.head = tmp_node
self.size += 1
return tmp_node
def remove(self, node):
if node == self.head:
self.head._next._pre = None
self.head = self.head._next
elif node == self.tail:
self.tail._pre._next = None
self.tail = self.tail._pre
else:
node._pre._next = node._next
node._next._pre = node._pre
self.size -= 1
def __str__(self):
str_text = ""
cur_node = self.head
while cur_node != None:
str_text += cur_node.data + " "
cur_node = cur_node._next
return str_text
实现LRU算法
插入数据时:若空间满了,则删除链表尾部元素,在进行插入
查询数据时:先把数据删除,再重新插入数据,保证了元素的顺序是按照访问顺序排列
class LRUCache:
def __init__(self, size):
self.size = size
self.hash_map = dict()
self.link = DoublyLink()
def set(self, key, value):
if self.size == self.link.size:
self.link.remove(self.link.tail)
if key in self.hash_map:
self.link.remove(self.hash_map.get(key))
tmp_node = self.link.insert(value)
self.hash_map.__setitem__(key, tmp_node)
def get(self, key):
tmp_node = self.hash_map.get(key)
self.link.remove(tmp_node)
self.link.insert(tmp_node)
return tmp_node.data
测试代码
r = LRUCache(3)
r.set("1", "1")
r.set("2", "2")
r.set("3", "3")
print r.link
r.get("1")
print r.link
r.set("4", "4")
print r.link
>> 3 2 1
>> 1 3 2
>> 4 1 3
LRU的Python实现(OrderedDict)
OrderedDict的本质就是一个有序的dict,其实现也是通过一个dict+双向链表
from collections import OrderedDict
class LRUCache:
def __init__(self, size):
self.size = size
self.linked_map = OrderedDict()
def set(self, key, value):
if key in self.linked_map:
self.linked_map.pop(key)
if self.size == len(self.linked_map):
self.linked_map.popitem(last=False)
self.linked_map.update({key: value})
def get(self, key):
value = self.linked_map.get(key)
self.linked_map.pop(key)
self.linked_map.update({key: value})
return value
测试代码
r = LRUCache(3)
r.set("1", "1")
r.set("2", "2")
r.set("3", "3")
print r.linked_map
r.get("1")
print r.linked_map
r.set("4", "4")
print r.linked_map
>> OrderedDict([('1', '1'), ('2', '2'), ('3', '3')])
>> OrderedDict([('2', '2'), ('3', '3'), ('1', '1')])
>> OrderedDict([('3', '3'), ('1', '1'), ('4', '4')])