LRU LRU(L east R ecently U sed,最近最少使用)是一种经典的缓存淘汰算法 。
它的核心思想是基于局部性原理:“如果数据最近被访问过,那么将来被访问的几率也更高”。当缓存容量达到上限,必须清理空间来存放新数据时,LRU 会优先淘汰距离当前时间最久没有被访问过 的数据。
底层实现逻辑
标准的 LRU 缓存通常通过 哈希表(Hash Map) + 双向链表(Doubly Linked List) 来实现,以保证所有操作的时间复杂度都是 O(1):
哈希表 :键指向链表中的节点,用于快速定位数据。
双向链表 :用于维护数据的访问顺序。每次新插入或被访问的数据,都会被移动到链表头部 (代表最新访问)。当缓存满时,直接删除链表尾部 的节点(代表最久未访问)。
https://leetcode.cn/problems/lru-cache/description/
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
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 import java.util.HashMap; class LRUCache { //双向链表节点 static class DLinkedNode{ int key; int value; DLinkedNode prev; DLinkedNode next; public DLinkedNode(int key, int value){ this.key = key; this.value = value; } } private HashMap<Integer,DLinkedNode> cache; private DLinkedNode dummyHead; private DLinkedNode dummyTail; private int capacity; private int size; public LRUCache(int capacity) { this.capacity = capacity; size = 0; cache = new HashMap<>(); //初始化虚拟头尾 dummyHead = new DLinkedNode(-1,-1); dummyTail = new DLinkedNode(-1,-1); dummyHead.next = dummyTail; dummyTail.prev = dummyHead; } //将node加到虚拟头后面 private void addToHead(DLinkedNode node){ node.prev = dummyHead; node.next = dummyHead.next; dummyHead.next.prev = node; dummyHead.next = node; } //摘除某个节点 private void removeNode(DLinkedNode node){ DLinkedNode pre = node.prev; DLinkedNode nex = node.next; pre.next = nex; nex.prev = pre; } //移动到头部 private void moveToHead(DLinkedNode node){ removeNode(node); addToHead(node); } //删除尾部节点,返回被删节点 private DLinkedNode removeTail(){ DLinkedNode tail = dummyTail.prev; removeNode(tail); return tail; } public int get(int key) { if(!cache.containsKey(key)){ return -1; } DLinkedNode node = cache.get(key); moveToHead(node); return node.value; } public void put(int key, int value) { if(cache.containsKey(key)){ //已存在,更新值+挪到头部 DLinkedNode node = cache.get(key); node.value = value; moveToHead(node); }else{ DLinkedNode newNode = new DLinkedNode(key,value); cache.put(key,newNode); addToHead(newNode); size++; if(size > capacity){ //淘汰尾结点 DLinkedNode remove = removeTail(); cache.remove(remove.key); size--; } } } }