相关:
学到的东西
- 链表头加dummy结点方便删除操作
- this.key = key; //前一个用this.是为了访问实例域,若不如此,key = key都是参数变量。(见java核心技术p126)
- 在自己的class里可以直接访问自己的private变量,比如LRUCache构造方法中,可以直接给private变量count赋值。
- Map api
- map.containsKey(key)
- map.get(key)
- map.put(key,value)
- map.remove(key)
- 建立一个HashMap:
- private Map<Integer,ListNode> map;
- map = new HashMap<>();
- 在牛客问题上学到了:
- 如何搞定输入参数的保存
- 要仔细读题!此题与LC146有一点点不同
LRU Cache思路总览:以LC146为例
-
数据结构的定义:
-
双向链表形式,靠近头部->旧,靠近尾部->新
-
put方法:
-
一切开始之前:先判断capacity是否正常!capacity<=0,直接返回!
- put的结点key是否已经存在?(通过map.containsKey(key)快速查找)
- 存在:更新value,视为内存使用,故将put进去的node转移到链表尾
- 新node是不是链表尾?
- 是:数据结构无需改变
- 不是:需要转移node
- 新node是不是链表尾?
- 不存在:
- 1.将新结点添加到链表尾
- 2.Map中增加一对key-node对
- 3.审核未加结点时链表长度是否<=capacity
- 链表长度超过capacity:需要进行删头操作removeHead(),同时删除map中对应项
- 链表长度未超过:计数器count++ 即可
- 存在:更新value,视为内存使用,故将put进去的node转移到链表尾
- put的结点key是否已经存在?(通过map.containsKey(key)快速查找)
-
get方法
-
一切开始之前,先判断capacity是否正常!不正常就返回-1!
- 结点是否在链表中?(map.containsKey(key))
- 存在:那么结点是否在链表尾?(get视为一次内存使用,是否需要更新链表结构?)
- 结点不在链表尾:那么需要把结点转移到链表尾,之后返回get到的value
- 结点在链表尾:那么不需要更新链表,直接返回get到的value
- 结点不在链表中:返回-1
- 存在:那么结点是否在链表尾?(get视为一次内存使用,是否需要更新链表结构?)
- 结点是否在链表中?(map.containsKey(key))
LeetCode146问题的解答
我踩过的坑点:
- 定义map:private Map<Integer,ListNode> map; //不要写成=map
- removeHead中要记得把map中的项也删除
- put时将已有结点转移到尾部,分为两步走:1.删除已有结点 2.结点插入到尾部
public class LRUCache{
public class ListNode{
int key;
int val;
ListNode prev;
ListNode next;
public ListNode(int key,int val){
this.key = key;
this.val = val;
}
}
private int capacity;
private int count;
private ListNode dummy;
private ListNode tail;
private Map<Integer,ListNode> map;//ERROR1:不要写成=map
public LRUCache(int capacity){
this.capacity = capacity;
count = 0;
dummy = new ListNode(0,0);
tail = dummy;
map = new HashMap<>();
}
public void removeHead(){
map.remove(dummy.next.key);//记住要删除表内的项!
dummy.next = dummy.next.next;
if(dummy.next!=null) dummy.next.prev = dummy;
else return;
}
public void appendTail(ListNode node){
tail.next = node;
node.prev = tail;
node.next = null;
tail = node;
}
public int get(int key){
if(map.containsKey(key)){
ListNode node = map.get(key);
if(node!=tail){
node.prev.next = node.next;
node.next.prev = node.prev;
appendTail(node);
return node.val;
}
else{//node is tail
return node.val;
}
}
else{
return -1;
}
}
public void put(int key,int value){
if(map.containsKey(key)){
ListNode node = map.get(key);
node.val = value;/*NOTE:key相同,value不同,要更新value!!*/
if(node != tail){
node.prev.next = node.next;
node.next.prev = node.prev;
appendTail(node);/*NOTE:要记得把node挪到尾部!*/
}
else return;
}
else{
ListNode node = new ListNode(key,value);
map.put(key,node);
appendTail(node);
if(count < capacity){
count++;
}
else{
removeHead();
}
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
牛客网LRU Cache问题的解答
我踩过的坑点:
- 此题特性:put的结点,key和已有结点重复,更新value的情况,不算是访问缓存中的数据,从而不需要对链表进行操作。
- 需要额外留意capacity<=0的情况,单独考虑此边界条件。不然会报空指针的错误。
/*链接:https://www.nowcoder.com/questionTerminal/3da4aeb1c76042f2bc70dbcb94513338
来源:牛客网
第一行读入一个整数n,表示LRU Cache的容量限制。 从第二行开始一直到文件末尾,每1行代表1个操作。
如果每行的第1个字符是p,则该字符后面会跟随2个整数,表示put操作的key和value。
如果每行的第1个字符是g,则该字符后面会跟随1个整数,表示get操作的key。
*/
import java.util.Scanner;
import java.util.Map;
import java.util.HashMap;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int capacity = sc.nextInt();
LRUCache cache = new LRUCache(capacity);
while(sc.hasNextLine()){
String input[] = sc.nextLine().trim().split(" ");
if(input[0].equals("p")){
int key = Integer.parseInt(input[1]);
int val = Integer.parseInt(input[2]);
cache.put(key,val);
}
else if(input[0].equals("g")){
int key = Integer.parseInt(input[1]);
System.out.println(cache.get(key));
}
}
sc.close();
}
public static class LRUCache{
public class ListNode{
int key;
int val;
ListNode prev;
ListNode next;
public ListNode(int key,int val){
this.key = key;
this.val = val;
}
}
private int capacity;
private int count;
private ListNode dummy;
private ListNode tail;
private Map<Integer,ListNode> map;//ERROR1:不要写成=map
public LRUCache(int capacity){
this.capacity = capacity;
count = 0;
dummy = new ListNode(0,0);
tail = dummy;
map = new HashMap<>();
}
public void removeHead(){
map.remove(dummy.next.key);//记住要删除表内的项!
dummy.next = dummy.next.next;
if(dummy.next!=null) dummy.next.prev = dummy;
else return;
}
public void appendTail(ListNode node){
tail.next = node;
node.prev = tail;
node.next = null;
tail = node;
}
public int get(int key){
if (capacity <= 0) return -1;
if(map.containsKey(key)){
ListNode node = map.get(key);
if(node!=tail){
node.prev.next = node.next;
node.next.prev = node.prev;
appendTail(node);
return node.val;
}
else{//node is tail
return node.val;
}
}
else{
return -1;
}
}
public void put(int key,int value){
if (capacity <= 0) return;
if(map.containsKey(key)){
ListNode node = map.get(key);
node.val = value;//NOTE:key相同,value不同,要更新value!!
//NOTE:此题特性:更新value不算使用,故链表结构不变
return;
}
else{
ListNode node = new ListNode(key,value);
map.put(key,node);
appendTail(node);
if(count < capacity){
count++;
}
else{
removeHead();
}
}
}
}
}