LRU 算法實際上是讓你設計數據結構:首先要接收一個 capacity 參數作為緩存的最大容量,然后實現兩個 API,一個是 put(key, val) 方法存入鍵值對,另一個是 get(key) 方法獲取 key 對應的 val,如果 key 不存在則返回 -1。
注意哦,get 和 put 方法必須都是 O(1) 的時間復雜度,我們舉個具體例子來看看 LRU 算法怎么工作。
/* 緩存容量為 2 */ LRUCache cache = new LRUCache(2); // 你可以把 cache 理解成一個隊列 // 假設左邊是隊頭,右邊是隊尾 // 最近使用的排在隊頭,久未使用的排在隊尾 // 圓括號表示鍵值對 (key, val) cache.put(1, 1); // cache = [(1, 1)] cache.put(2, 2); // cache = [(2, 2), (1, 1)] cache.get(1); // 返回 1 // cache = [(1, 1), (2, 2)] // 解釋:因為最近訪問了鍵 1,所以提前至隊頭 // 返回鍵 1 對應的值 1 cache.put(3, 3); // cache = [(3, 3), (1, 1)] // 解釋:緩存容量已滿,需要刪除內容空出位置 // 優先刪除久未使用的數據,也就是隊尾的數據 // 然后把新的數據插入隊頭 cache.get(2); // 返回 -1 (未找到) // cache = [(3, 3), (1, 1)] // 解釋:cache 中不存在鍵為 2 的數據 cache.put(1, 4); // cache = [(1, 4), (3, 3)] // 解釋:鍵 1 已存在,把原始值 1 覆蓋為 4 // 不要忘了也要將鍵值對提前到隊頭
分析上面的操作過程,要讓 put 和 get 方法的時間復雜度為 O(1),我們可以總結出 cache 這個數據結構必要的條件:查找快,插入快,刪除快,有順序之分。
因為顯然 cache 必須有順序之分,以區分最近使用的和久未使用的數據;而且我們要在 cache 中查找鍵是否已存在;如果容量滿了要刪除最后一個數據;每次訪問還要把數據插入到隊頭。
那么,什么數據結構同時符合上述條件呢?哈希表查找快,但是數據無固定順序;鏈表有順序之分,插入刪除快,但是查找慢。所以結合一下,形成一種新的數據結構:哈希鏈表。
LRU 緩存算法的核心數據結構就是哈希鏈表,雙向鏈表和哈希表的結合體。這個數據結構長這樣:
需要刪除操作。刪除一個節點不光要得到該節點本身的指針,也需要操作其前驅節點的指針,而雙向鏈表才能支持直接查找前驅,保證操作的時間復雜度 O(1)。
''' 方法一 class LRUCache: #@param capacity,an integer def __init__(self,capacity): self.cache ={} self.used_list=[] self.capacity = capacity #@return an integer def get(self,key): if key in self.cache: #使用一個list來記錄訪問的順序,最先訪問的放在list的前面,最后訪問的放在list的后面,故cache已滿時,則刪除list[0],然后插入新項; if key != self.used_list[-1]: self.used_list.remove(key) self.used_list.append(key) return self.cache[key] else: return -1 def put(self,key,value): if key in self.cache: self.used_list.remove(key) elif len(self.cache) == self.capacity: self.cache.pop(self.used_list.pop(0)) self.used_list.append(key) self.cache[key] = value ''' #方法二: import collections #基於orderedDict實現 class LRUCache(collections.OrderedDict): ''' function:利用collection.OrdereDict數據類型實現最近最少使用的算法 OrdereDict有個特殊的方法popitem(Last=False)時則實現隊列,彈出最先插入的元素 而當Last=True則實現堆棧方法,彈出的是最近插入的那個元素。 實現了兩個方法:get(key)取出鍵中對應的值,若沒有返回None set(key,value)更具LRU特性添加元素 ''' def __init__(self,size=5): self.size = size self.cache = collections.OrderedDict()#有序字典 def get(self,key): if key in self.cache.keys(): #因為在訪問的同時還要記錄訪問的次數(順序) value = self.cache.pop(key) #保證最近訪問的永遠在list的最后面 self.cache[key] = value return value else: value = None return value def put(self,key,value): if key in self.cache.keys(): self.cache.pop(key) self.cache[key] = value elif self.size == len(self.cache): self.cache.popitem(last=False) self.cache[key] = value else: self.cache[key] = value if __name__ == '__main__': test = LRUCache() test.put('a',1) test.put('b',2) test.put('c',3) test.put('d',4) test.put('e',5) # test.put('f',6) print (test.get('a'))
參考:https://blog.csdn.net/qq_35810838/article/details/83035759
https://labuladong.gitbook.io/algo/gao-pin-mian-shi-xi-lie/lru-suan-fa