By
木子雷
阅读数: 次
前言:
什么是LRU算法:LRU是Least Recently Used的缩写,即最近最久未使用,是一种操作系统中常用的页面置换算法。
应用场景:
知道了什么是LRU后,我们再来聊下它的使用场景;在工作中,对于Redis我们一定是比较熟悉的,它是一个内存数据库;因为它是内存数据库,并且内存的空间是有限的,如果Redis中数据量很大的话,内存就可能被占满,但是此时如果还有数据存入Redis的话,那该怎么办呢?这就是由Redis的的内存淘汰策略所决定的。
LRU最近最久未使用算法就是Redis的内存淘汰策略之一。
示例:
1 2 3 4 5 6 7 8 9 10 11 12
| LRUCache cache = new LRUCache( 2 ); cache.put(1, 1); cache.put(2, 2); cache.get(1); cache.put(3, 3); cache.get(2); cache.put(4, 4); cache.get(1); cache.get(3); cache.get(4);
|
设计LRU算法的数据结构:
1、要求:
①、首先支持查询数据get和写入数据put;
②、满足时间复杂度为O(1);
2、思路:
由题目中要求的O(1)时间复杂度想到缓存可以想到用一个map来存储key、value结点,最近最久未使用到的(缓存数据)放到最后,最新访问的(缓存数据)放到最前面,可以考虑用双向链表来实现,这样,这个map的key对应的是缓存的Key, value对应的是双向链表的一个节点,即链表的节点同时存在map的value中。
这样,当新插入一个节点时,它应该在这个双向链表的头结点处,同时把这个节点的key和这个节点put到map中保留下来。当LRU缓存链表容量达到最大又要插入新节点时,把链表的尾节点删除掉,同时在map中移除该节点对应的key。
双向链表中节点的数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class DoubleLinkedListNode { String key; Object value; DoubleLinkedListNode pre; DoubleLinkedListNode next; public DoubleLinkedListNode(String key, Object value) { this.key = key; this.value = value; } }
|
由此可以抽象出LRU缓存算法的数据结构:双向链表+HashMap。
数据结构逻辑图如下所示:
代码奉上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| public class LRUCache { private HashMap<String, DoubleLinkedListNode> map = new HashMap<String, DoubleLinkedListNode>(); private DoubleLinkedListNode head; private DoubleLinkedListNode tail; private int capacity; private int size; public LRUCache(int capacity) { this.capacity = capacity; size = 0; }
public void setHead(DoubleLinkedListNode node) { node.next = head; node.pre = null; if (head != null) { head.pre = node; } head = node; if (tail == null) { tail = node; } }
public void removeNode(DoubleLinkedListNode node) { DoubleLinkedListNode cur = node; DoubleLinkedListNode pre = cur.pre; DoubleLinkedListNode post = cur.next; if (pre != null) { pre.next = post; } else { head = post; } if (post != null) { post.pre = pre; } else { tail = pre; } }
public Object get(String key) { if (map.containsKey(key)) { DoubleLinkedListNode node = map.get(key); removeNode(node); setHead(node); return node.value; } return null; }
public void set(String key, Object value) { if (map.containsKey(key)) { DoubleLinkedListNode node = map.get(key); node.value = value; removeNode(node); setHead(node); } else { DoubleLinkedListNode newNode = new DoubleLinkedListNode(key, value); if (size < capacity) { setHead(newNode); map.put(key, newNode); size++; } else { map.remove(tail.key); removeNode(tail); setHead(newNode); map.put(key, newNode); } } }
public static void main(String[] args) { System.out.println("双向链表的容量为6"); LRUCache lc = new LRUCache(6); for (int i = 0; i < 6; i++) { lc.set("test" + i, "test" + i); } System.out.println("第一次遍历双向链表:(从头结点遍历到尾节点)"); DoubleLinkedListNode node = lc.head; while (node != null) { System.out.print(node.key + " "); node = node.next; } System.out.println(); lc.get("test2"); System.out.println(); System.out.println("get查询 test2节点后 ,第二次遍历双向链表:"); DoubleLinkedListNode node1 = lc.head; while (node1 != null) { System.out.print(node1.key + " "); node1 = node1.next; } System.out.println(); lc.set("sucess", "sucess");
System.out.println(); System.out.println("put插入sucess节点后,第三次遍历双向链表:"); DoubleLinkedListNode node2 = lc.head; while (node2 != null) { System.out.print(node2.key + " "); node2 = node2.next; } } }
|
运行结果展示:
1 2 3 4 5 6 7 8 9
| 双向链表容量为6 第一次遍历双向链表:(从头结点遍历到尾节点) test5 test4 test3 test2 test1 test0
get查询 test2节点后 ,第二次遍历双向链表: test2 test5 test4 test3 test1 test0
put插入sucess节点后,第三次遍历双向链表: sucess test2 test5 test4 test3 test1
|
参考资料:
1、记一次阿里面试,我挂在了 最熟悉不过的LRU 缓存算法设计上。。。。。
2、 【LeetCode】146. LRU缓存机制
3、LRU Cache leetcode java