LRU算法在后端工程师面试中,是一个比较常出现的题目,这篇文章带你们一块儿,理解LRU算法,并最终用Python轻松实现一个基于LRU算法的缓存。python
先看一张图,当咱们访问网页,浏览器会给服务器发请求,服务器会通过一系列的运算,把页面返回给浏览器。面试
当有多个浏览器同时访问的时候,就会在短期内发起多个请求,而服务器对每个请求都要进行一系列相同的操做。重复工做不只浪费资源,还可能致使响应速度变慢。算法
而缓存则能够把服务器返回的页面保存下来,当有其余的浏览器再访问时候,就没必要劳服务器大驾,直接由缓存返回页面。为了保证响应速度,缓存一般是基于比较昂贵的硬件,好比RAM,这就决定了咱们很难用大量的缓存把全部的页面都存下来,当刚好没有缓存浏览器请求的页面时,依然须要请求服务器。因为缓存容量有限,而数据量无限(互联网天天新产生的页面数没法估计),就须要把好刚用在刀刃上,缓存那些最有用的信息。后端
LRU是一种缓存淘汰算法(在OS中也叫内存换页算法),因为缓存空间是有限的,因此要淘汰缓存中不经常使用的数据,留下经常使用的数据,达到缓存效率的最大化。LRU就是这样一种决定“淘汰谁留下谁”的算法,LRU是Least recently used的缩写,从字面意思“最近最少使用”,咱们就能够理解LRU的淘汰规则。浏览器
咱们用一张图来描述LRU的淘汰逻辑,图中的缓存是一个列表结构,上面是头结点下面是尾节点,缓存容量为8(8个小格子):缓存
按上面的逻辑咱们能够看到,一个数据若是常常被访问就会不断地被移动到列表头部,不会被淘汰出缓存,而越不常常访问的数据,越容易被挤出缓存。服务器
接下来咱们用Python来实现一个采用LRU算法的缓存。数据结构
从前面的文章中咱们能够知道,缓存简化下来就两个功能,一个是往里装数据(缓存数据),一个是往外吐数据(命中缓存),因此咱们的缓存对外只须要put和get两个接口就能够了。性能
按照前面的示意图,缓存内部咱们只须要有一个列表(list)就能够实现LRU逻辑,不过用列表虽然能实现逻辑,可是在判断是否命中缓存时,速度可能很是慢(列表须要遍历才能知道数据有没有在里面)。在Python中,咱们能够用基于hash的结构,好比字典(dict)或集合(set),来快速判断数据是否存在,解决列表实现的性能问题。可是字典和集合又是没有顺序的,若是能有一种既能排序,又是基于hash存储的数据结构,就行了。spa
在Python的collections包中,已经内置了这种实用的结构OrderedDict,OrderedDict是dict的子类,可是存储在内部的元素是有序的(列表的特色)。
解决了数据结构的问题,咱们能够直接上手写逻辑了,代码以下:
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.queue = collections.OrderedDict()
def get(self, key):
if key not in self.queue:
return -1 // 要找的数据不在缓存中返回-1
value = self.queue.pop(key) // 将命中缓存的数据移除
self.queue[key] = value // 将命中缓存的数据从新添加到头部
return self.queue[key]
def put(self, key, value):
if key in self.queue: // 若是已经在缓存中,则先移除老的数据
self.queue.pop(key)
elif len(self.queue.items()) == self.capacity:
self.queue.popitem(last=False) // 若是不在缓存中而且到达最大容量,则把最后的数据淘汰
self.queue[key] = value // 将新数据添加到头部
复制代码
下次面试在遇到LRU的题目,是否是就成竹在胸了?