| 
 | 1 | +## LRU缓存  | 
 | 2 | +### 题目描述  | 
 | 3 | + | 
 | 4 | +运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。  | 
 | 5 | +获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。  | 
 | 6 | +写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。  | 
 | 7 | + | 
 | 8 | +示例:  | 
 | 9 | +```  | 
 | 10 | +LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );  | 
 | 11 | + | 
 | 12 | +cache.put(1, 1);  | 
 | 13 | +cache.put(2, 2);  | 
 | 14 | +cache.get(1); // 返回 1  | 
 | 15 | +cache.put(3, 3); // 该操作会使得密钥 2 作废  | 
 | 16 | +cache.get(2); // 返回 -1 (未找到)  | 
 | 17 | +cache.put(4, 4); // 该操作会使得密钥 1 作废  | 
 | 18 | +cache.get(1); // 返回 -1 (未找到)  | 
 | 19 | +cache.get(3); // 返回 3  | 
 | 20 | +cache.get(4); // 返回 4  | 
 | 21 | +```  | 
 | 22 | + | 
 | 23 | +### 解法  | 
 | 24 | +用一个哈希表和一个双向链表来实现  | 
 | 25 | +- 哈希表保存每个节点的地址  | 
 | 26 | +- 链表头表示上次访问距离现在时间最短,尾部表示最近访问最少  | 
 | 27 | +- 访问节点,若节点存在,要把该节点放到链表头  | 
 | 28 | +- 插入时要考虑是否超过容量  | 
 | 29 | + | 
 | 30 | +```java  | 
 | 31 | +//双向链表的节点  | 
 | 32 | +class Node{  | 
 | 33 | + public int key;  | 
 | 34 | + public int val;  | 
 | 35 | + public Node pre;//指向前面的指针  | 
 | 36 | + public Node next;//指向后面的指针  | 
 | 37 | + public Node(int key,int value){  | 
 | 38 | + this.val = value;  | 
 | 39 | + this.key = key;  | 
 | 40 | + }  | 
 | 41 | +}  | 
 | 42 | +class LRUCache {  | 
 | 43 | + int capacity;//容量  | 
 | 44 | + Node head;//双向链表的头,维护这个指针,因为set,get时需要在头部操作  | 
 | 45 | + Node end;//双向链表的尾,set时,要是满了,需要将链表的最后一个节点remove  | 
 | 46 | + HashMap<Integer,Node> map = new HashMap<Integer,Node>();//hash表  | 
 | 47 | + public LRUCache(int capacity) {  | 
 | 48 | + this.capacity = capacity;  | 
 | 49 | + }  | 
 | 50 | + //添加,删除尾部,插入头部的操作  | 
 | 51 | + public void remove(Node node){  | 
 | 52 | + Node cur = node;  | 
 | 53 | + Node pre = node.pre;  | 
 | 54 | + Node post = node.next;  | 
 | 55 | + if(pre == null){//说明cur是头部节点  | 
 | 56 | + head = post;  | 
 | 57 | + }  | 
 | 58 | + else pre.next = post;//更新指针,删除  | 
 | 59 | + if(post == null){//说明cur是最后的节点  | 
 | 60 | + end = pre;  | 
 | 61 | + }  | 
 | 62 | + else post.pre = pre;  | 
 | 63 | + }  | 
 | 64 | + public void setHead(Node node){  | 
 | 65 | + //直接插入  | 
 | 66 | + node.next = head;  | 
 | 67 | + node.pre = null;  | 
 | 68 | + if(head != null) head.pre = node;//防止第一次插入时为空  | 
 | 69 | + head = node;  | 
 | 70 | + if(end==null) end = node;  | 
 | 71 | + }  | 
 | 72 | + public int get(int key) {  | 
 | 73 | + if(map.containsKey(key)){  | 
 | 74 | + //需要把对应的节点调整到头部  | 
 | 75 | + Node latest = map.get(key);  | 
 | 76 | + remove(latest);  | 
 | 77 | + setHead(latest);  | 
 | 78 | + //返回value  | 
 | 79 | + return latest.val;  | 
 | 80 | + }  | 
 | 81 | + else return -1;  | 
 | 82 | + }  | 
 | 83 | + | 
 | 84 | + public void put(int key, int value) {  | 
 | 85 | + if(map.containsKey(key)){//这个key原来存在  | 
 | 86 | + //只需要把key对应的node提到最前面,更新value  | 
 | 87 | + Node oldNode = map.get(key);  | 
 | 88 | + oldNode.val = value;  | 
 | 89 | + remove(oldNode);  | 
 | 90 | + setHead(oldNode);  | 
 | 91 | + }  | 
 | 92 | + else{  | 
 | 93 | + //这个key原来不存在,需要重新new出来  | 
 | 94 | + Node newNode = new Node(key,value);  | 
 | 95 | + //接下来要考虑容量  | 
 | 96 | + if(map.size() < capacity){  | 
 | 97 | + setHead(newNode);  | 
 | 98 | + map.put(key, newNode);  | 
 | 99 | + }  | 
 | 100 | + else{  | 
 | 101 | + //容量不够,需要先将map中,最不常使用的那个删除了删除  | 
 | 102 | + map.remove(end.key);  | 
 | 103 | + //接下来更新双向链表  | 
 | 104 | + remove(end);  | 
 | 105 | + setHead(newNode);  | 
 | 106 | + //放入新的  | 
 | 107 | + map.put(key, newNode);  | 
 | 108 | + }  | 
 | 109 | + }  | 
 | 110 | + }  | 
 | 111 | +}  | 
 | 112 | + | 
 | 113 | +/**  | 
 | 114 | + * Your LRUCache object will be instantiated and called as such:  | 
 | 115 | + * LRUCache obj = new LRUCache(capacity);  | 
 | 116 | + * int param_1 = obj.get(key);  | 
 | 117 | + * obj.put(key,value);  | 
 | 118 | + */  | 
 | 119 | +```  | 
0 commit comments