以前好几回接触到 LRU(Least Recently Used)算法,今天来总结下,并用 Java 和 Python 给出相应的实现。html
LRU是一种缓存替换算法,根据字面意思,就是将最近最少使用的页面或者元素进行替换,将最近最多使用的页面或者元素保持在缓存里。有关缓存的知识后面再仔细研究下。因为缓存的容量大小有限,这才有了LRU之类的缓存算法。还有一些其余的缓存算法,能够参考这个页面。算法
根据下面的图示进行LRU算法的理解。缓存
其中 put 操做用于将最近使用的元素放置在缓存中。必须先判断 key 是否存在,若是存在,则删除,再添加;若不存在,则直接添加,而后判断添加后的缓存是否超过了容量;若超出,则删除最远元素;get 操做用于获取缓存中元素的值,在 leetcode146 题中规定,若是缓存中没有该元素,则返回 -1。this
通常咱们在实现的时候会考虑存储 key-value 的键值对形式,能够用双链表存储 key,HashMap 存储真正须要的值 value,因此真正意义上的缓存应该是指这个HashMap。链表的做用是用来顺序存储 key,当缓存满了,须要删除最远的那个 key 及其 value,此时就须要根据链表找到最远的 value 的 key,从而删除缓存 HashMap中的最远的键值对。spa
这里咱们用 双链表 + hashMap 以及 LinkedHashMap 、Python 中 OrderedDict 三种方式来实现一个简单的 LRU 机制。code
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)的复杂度。htm
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并删除其键值对。blog
Python 能够使用这篇文章介绍的 OrderedDict 这一字典子类很轻松的实现 LRU 机制。ip
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