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的題目,是否是就成竹在胸了?