LRU算法实现
1.lru缓存策略
构成要素有两个:内存大小和替换策略.
实现方法: hashtable+双向链表
查询时间复杂度o(1),修改更新时间复杂度o(1).
2.lru工作原理
1 1.题目地址:https://leetcode-cn.com/problems/lru-cache/ 2 2.题目内容:LRU缓存机制(中等) 3 4 运用你所掌握的数据结构,设计和实现一个LRU(最近最少使用)缓存机制.它应该支持以下操作:获取数据get和写入数据put. 5 6 获取数据get(key)-如果关键字(key)存在于缓存中,则获取关键字的值(总是正数),否则返回-1. 7 写入数据put(key,value)-如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组[关键字/值].当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间. 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 4.lru(哈希表+双向链表) 27 28 public class Lru { 29 // 1.定义双向链表 30 class DoubleLinkNode { 31 int key; 32 int value; 33 DoubleLinkNode prev; 34 DoubleLinkNode next; 35 36 public DoubleLinkNode() {} 37 38 public DoubleLinkNode(int key, int value) { 39 this.key = key; 40 this.value = value; 41 } 42 } 43 44 // 2.定义哈希表 45 private Map<Integer, DoubleLinkNode> cache = new HashMap<>(); 46 // 3.定义缓存数量,缓存容量 47 private int size; 48 private int capacity; 49 // 4.定义头尾节点 50 private DoubleLinkNode head; 51 private DoubleLinkNode tail; 52 53 public Lru(int capacity) { 54 this.size = 0; 55 this.capacity = capacity; 56 // 设置伪头部和尾部,非常巧妙 57 head = new DoubleLinkNode(); 58 tail = new DoubleLinkNode(); 59 head.next = tail; 60 tail.prev = head; 61 } 62 63 /** 64 * 将节点写入头部 65 * A-B-C,其中AC插入B,需要处理B的prv和next节点,C的prev,A的next 66 */ 67 private void addToHead(DoubleLinkNode node) { 68 node.prev = head; 69 node.next = head.next; 70 // 下面顺序不可颠倒 71 head.next.prev = node; 72 head.next = node; 73 } 74 75 /** 76 * 删除结点 77 * A-B-C,其中ABC删除B,需要处理A的next和c的prev 78 */ 79 private void removeNode(DoubleLinkNode node) { 80 node.prev.next = node.next; 81 node.next.prev = node.prev; 82 } 83 84 /** 85 * 移动到头结点 86 * A-B-C,其中AC插入B,需要处理B的prv和next节点,A的next,C的prev 87 */ 88 private void moveToHead(DoubleLinkNode node) { 89 removeNode(node); 90 addToHead(node); 91 } 92 93 /** 94 * 删除尾结点 95 * @return DoubleLinkNode 96 */ 97 private DoubleLinkNode removeTail() { 98 DoubleLinkNode node = tail.prev; 99 removeNode(node); 100 return node; 101 } 102 103 /** 104 * 获取缓存值 105 * @param key 106 */ 107 public int get(int key) { 108 DoubleLinkNode node = cache.get(key); 109 if (node == null) { 110 return -1; 111 } else { 112 addToHead(node); 113 return node.value; 114 } 115 } 116 117 /** 118 * 保存缓存值 119 * @param key 120 */ 121 public void put(int key, int value) { 122 DoubleLinkNode node = cache.get(key); 123 // 如果节点存在 124 if (node != null) { 125 node.value = value; 126 moveToHead(node); 127 } else { 128 // 创建新节点,添加到hash表中 129 DoubleLinkNode newNode = new DoubleLinkNode(key, value); 130 cache.put(key, newNode); 131 // 移动到双向链表头部 132 moveToHead(newNode); 133 size++; 134 if (size > capacity) { 135 // hash表超出容量 136 DoubleLinkNode tail = removeTail(); 137 cache.remove(tail.key); 138 size--; 139 } 140 } 141 } 142 }