LRU 緩存機制及 3 種簡單實現


  之前好幾次接觸到 LRU(Least Recently Used)算法,今天來總結下,並用 Java 和 Python 給出相應的實現。

  LRU是一種緩存替換算法,根據字面意思,就是將最近最少使用的頁面或者元素進行替換,將最近最多使用的頁面或者元素保持在緩存里。有關緩存的知識后面再仔細研究下。由於緩存的容量大小有限,這才有了LRU之類的緩存算法。還有一些其他的緩存算法,可以參考這個頁面

  根據下面的圖示進行LRU算法的理解。

 

  其中 put 操作用於將最近使用的元素放置在緩存中。必須先判斷 key 是否存在,如果存在,則刪除,再添加;若不存在,則直接添加,然后判斷添加后的緩存是否超過了容量;若超出,則刪除最遠元素;get 操作用於獲取緩存中元素的值,在 leetcode146 題中規定,如果緩存中沒有該元素,則返回 -1。

  一般我們在實現的時候會考慮存儲 key-value 的鍵值對形式,可以用雙鏈表存儲 key,HashMap 存儲真正需要的值 value,所以真正意義上的緩存應該是指這個HashMap。鏈表的作用是用來順序存儲 key,當緩存滿了,需要刪除最遠的那個 key 及其 value,此時就需要根據鏈表找到最遠的 value 的 key,從而刪除緩存 HashMap中的最遠的鍵值對。

  這里我們用 雙鏈表 + hashMap 以及 LinkedHashMap 、Python 中 OrderedDict 三種方式來實現一個簡單的 LRU 機制。

雙鏈表 + hashMap

 

 1 class Solution {  2     private LinkedList<Integer> linkedList;  3     private Map<Integer, Integer> map;  4 
 5     private int max_size;  6     private int cur_size = 0;  7 
 8     public Solution(int capacity) {  9         linkedList = new LinkedList<>(); 10         map = new HashMap<>(); 11         this.max_size = capacity; 12  } 13 
14     public int get(int key) { 15         if(!map.containsKey(key)){ 16             return -1; 17  } 18 
19         int val = map.get(key); 20         Object o = key; 21  linkedList.remove(o); 22  linkedList.addLast(key); 23         return val; 24  } 25 
26     public void put(int key, int value) { 27         if(map.containsKey(key)){ 28             // 這個put不能省略,即時key存在,若新添加的value更新了,那剛好就將value更新,如果省略,則value更新不了
29  map.put(key, value); 30             Object o = key; 31  linkedList.remove(o); 32  linkedList.addLast(key); 33         }else{ 34  map.put(key, value); 35             cur_size++; 36  linkedList.addLast(key); 37             if(cur_size>max_size){ 38                 int tmp = linkedList.removeFirst(); 39  map.remove(tmp); 40                 cur_size--; 41  } 42  } 43  } 44 }

 

  其中在進行元素刪除的時候,鏈表的時間復雜度是O(n),用 HashMap 進行 key 的查找的時候是O(1)的復雜度。

LinkedHashMap

 1 class Solution {  2     private LinkedHashMap<Integer, Integer> map;  3     private int max_size;  4     private int cur_size;  5 
 6     public Solution(int capacity) {  7         map = new LinkedHashMap<>();  8         this.max_size = capacity;  9         this.cur_size = 0; 10  } 11 
12     public int get(int key) { 13         // 若沒有,則返回 -1
14         if(!map.containsKey(key)){ 15             return -1; 16  } 17 
18         int val = map.get(key); 19  map.remove(key); 20  map.put(key, val); 21         return val; 22  } 23 
24     public void put(int key, int value) { 25         if(map.containsKey(key)){ 26  map.remove(key); 27  map.put(key, value); 28         }else{ 29             cur_size++; 30  map.put(key, value); 31             if(cur_size > max_size){ 32                 int oldestKey = map.keySet().iterator().next(); // 獲取最遠的key。
33  map.remove(oldestKey); 34                 cur_size--; 35  } 36  } 37  } 38 }

  LinkedHashMap 本身也是由 雙鏈表 + hashMap 組成的。在緩存滿了需要刪除最遠的元素的時候,是用的 HashMap 里的迭代器來獲取最開始進來的key並刪除其鍵值對。

Python OrderedDict

  Python 可以使用這篇文章介紹的 OrderedDict 這一字典子類很輕松的實現 LRU 機制。

 1 class LRUCache:  2 
 3     def __init__(self, capacity: int):  4         self.dic = OrderedDict()  5         self.remain = capacity  6 
 7 
 8     def get(self, key: int) -> int:  9         if key not in self.dic: 10             return -1
11         # v = self.dic.pop(key)
12         # self.dic[key] = v
13         self.dic.move_to_end(key, last = True) 14         return self.dic[key] 15 
16     def put(self, key: int, value: int) -> None: 17         if key in self.dic: 18  self.dic.pop(key) 19         else: 20             if self.remain > 0: 21                 self.remain -= 1
22             else: 23                self.dic.popitem(last = False) 24         self.dic[key] = value

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM