题目
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案。
示例:
输入
[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
返回值
[1,-1]
分析
题目意思:
第一次操作后:最常使用的记录为(“1”, 1)
第二次操作后:最常使用的记录为(“2”, 2),(“1”, 1)变为最不常用的
第三次操作后:最常使用的记录为(“3”, 2),(“1”, 1)还是最不常用的
第四次操作后:最常用的记录为(“1”, 1),(“2”, 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录(“2”, 2),加入记录(“4”, 4),并且为最常使用的记录,然后(“3”, 2)变为最不常使用的记录
思路:利用map存储键和值,利用双向链表记录插入到缓存中的数据
- 利用java自带的LinkedHashMap,LinkedHashMap有一个重要的特性就是有序性,加入键值对的顺序是怎样的迭代输出就是什么顺序,其内部采用双向链表结构维持顺序。
import java.util.*;
public class Solution {
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
public int[] LRU (int[][] operators, int k) {
// write code here
Map<Integer, Integer> map = new LinkedHashMap<>();
List<Integer> list = new LinkedList<>();
for (int[] operator : operators) {
int key = operator[1];
switch(operator[0]) {
case 1: //插入操作
//获取值
int value = operator[2];
//如果缓存还有空间,直接加入
if (map.size() < k) {
map.put(key, value);
} else {//缓存没有空间,需要弹出一个元素,这里采用的是首元素是最不经常使用
//获取第一个插入的元素
Iterator it = map.keySet().iterator();
map.remove(it.next());
map.put(key, value);
}
break;
case 2://获取元素
if (map.containsKey(key)) {
int val = map.get(key);
list.add(val);
//移除这个元素并重新插入到链表的尾部
map.remove(key);
map.put(key, val);
} else {
list.add(-1);
}
break;
default:
}
}
int[] res = new int[list.size()];
int i = 0;
for (int val : list) {
res[i++] = val;
}
return res;
}
}
- 自己实现双向链表
import java.util.*;
public class Solution {
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
public int[] LRU (int[][] operators, int k) {
//存储<key,node>
Map<Integer,Node> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
//尾结点指针
Node last = null;
//头指针
Node head = new Node(-1,-1);
for(int[] opt:operators) {
//键
int key = opt[1];
switch (opt[0]){
//如果是set操作,将节点加入head后,并放入map,注意加入的可以是不相同的
case 1:
Node n = new Node(key,opt[2]);
map.put(key,n);
if(head.next == null) {
insertNode(head,n);
last = n;
} else {
insertNode(head,n);
}
if(map.size() > k) {
//移除最后一个元素
map.remove(last.key);
last = last.pre;
last.next = null;
}
break;
case 2://如果是get操作
Node t = map.get(key);
if(t != null) {
list.add(t.value);
//如果是尾结点且链表有多个节点
if(t.next == null && map.size() > 1) {
//更新尾结点
last = t.pre;
last.next = null;
//插入到头结点
insertNode(head,t);
} else if(t.next != null && map.size() > 1) {//如果是中间某个结点且长度大于1
Node preNode = t.pre;
Node nextNode = t.next;
preNode.next = nextNode;
nextNode.pre = preNode;
insertNode(head,t);
}
} else {
list.add(-1);
}
//System.out.println("case 2:");
//printNode(head);
//System.out.println();
break;
}
}
int[] res = new int[list.size()];
int j = 0;
for (int i : list) {
res[j++] = i;
}
return res;
}
//实现插入一个结点到头结点后
public void insertNode(Node head,Node insertNode) {
if(head.next == null) {
head.next = insertNode;
insertNode.pre = head;
insertNode.next = null;
} else {
Node nextNode = head.next;
insertNode.next = nextNode;
nextNode.pre = insertNode;
head.next = insertNode;
insertNode.pre = head;
}
}
}
class Node{
public Integer key;
public Integer value;
public Node pre;
public Node next;
public Node(Integer key,Integer value) {
this.key = key;
this.value = value;
this.pre = null;
this.next = null;
}
}