1. 题目
https://leetcode.cn/problems/lru-cache/
2. 解法
解题思路是
使用一个双向链表和一个哈希表来实现一个LRU缓存机制。
双向链表用来存储最常使用的键值对,最近使用的元素放在链表的表头,链表中最后一个元素是使用频率最低的元素。
哈希表用来记录对应的<key,<key, value>>,用于查找现在的缓存中是否有key及其value,以及快速定位链表中的节点。
当需要获取数据时,先在哈希表中查找是否有对应的key,如果有,则返回对应的value,并将该节点移动到链表的表头。如果没有,则返回-1。
当需要写入数据时,先在哈希表中查找是否有对应的key,如果有,则更新对应的value,并将该节点移动到链表的表头。如果没有,则在链表的表头插入新的节点,并在哈希表中添加新的映射。如果此时缓存已满,则删除链表的最后一个节点,并在哈希表中删除对应的映射。
这道题的解题思路可以分为以下几个步骤:
- 创建一个双向链表和一个哈希表。
- 定义双向链表的节点类,包含key,value,pre和next四个属性。
- 定义双向链表的数据结构,包含head,tail,size三个属性,以及addFirst,remove,removeLast和size四个方法。
- 定义LRU缓存的数据结构,包含map,cache和cap三个属性,以及get和put两个方法。
- 在get方法中,先在map中查找key是否存在,如果存在,则返回value,并将节点移动到链表头部;如果不存在,则返回-1。
- 在put方法中,先在map中查找key是否存在,如果存在,则更新value,并将节点移动到链表头部;如果不存在,则在链表头部添加新节点,并在map中添加映射;如果缓存已满,则删除链表尾部的节点,并在map中删除映射。
class LRUCache { //定义双向链表的节点类 class Node{ int key; int value; Node pre; Node next; public Node(int key,int value){ this.key=key; this.value=value; } } //定义双向链表的数据结构 class DoubleList{ //头尾虚节点 Node head,tail; //链表元素数 int size; public DoubleList(){ //初始化双向链表的数据 head=new Node(0,0); tail=new Node(0,0); head.next=tail; tail.pre=head; size=0; } //在链表头部添加节点x,时间O(1) public void addFirst(Node x){ x.next=head.next; x.pre=head; head.next.pre=x; head.next=x; size++; } //删除链表中的x节点(x一定存在) //由于是双链表且给的是目标Node节点,时间O(1) public void remove(Node x){ x.pre.next=x.next; x.next.pre=x.pre; size--; } //删除链表中最后一个节点,并返回该节点,时间O(1) public Node removeLast(){ if(tail.pre==head){ return null; } Node last=tail.pre; remove(last); return last; } //返回链表长度,时间O(1) public int size(){return size;} } //key 映射到 Node(key,val) private HashMap<Integer,Node> map; //Node(k1,k2) 双向链表:k1 - k2 - ... private DoubleList cache; //最大容量 private int cap; public LRUCache(int capacity) { map=new HashMap<>(); cache=new DoubleList(); this.cap=capacity; } /* 将某个key提升为最近使用的 */ private void makeRecently(int key){ Node x=map.get(key); //先从链表中删除这个节点 cache.remove(x); //重新插到队头 cache.addFirst(x); } /* 添加最近使用的元素 */ private void addRecently(int key,int val){ Node x=new Node(key,val); //链表头部就是最近使用的元素 cache.addFirst(x); //别忘了在map中添加key的映射 map.put(key,x); } /* 删除某一个key */ private void deleteKey(int key){ Node x=map.get(key); //从链表中删除 cache.remove(x); //从map中删除 map.remove(key); } /* 删除最久未使用的元素 */ private void removeLeastRecently(){ // 链表尾部的元素就是最久未使用的元素 Node deleteNode=cache.removeLast(); //别忘了从map中删除它的key int deletedKey=deleteNode.key; map.remove(deletedKey); } public int get(int key) { if(!map.containsKey(key)){ return -1; } //将该数据提升为最近使用的 makeRecently(key); return map.get(key).value; } public void put(int key, int value) { if(map.containsKey(key)){ //删除旧的数据 deleteKey(key); //新插入的数据为最近使用的数据 addRecently(key,value); return ; } if(cap==cache.size()){ //删除最久未使用的元素 removeLeastRecently(); } //添加为最近使用的元素 addRecently(key,value); } }