leetcode的LRU题目:146. LRU 缓存机制
问题描述:
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
分析
构建LRU数据结构,LRU由map、LinkedList构成,还需要一个类Node。
map负责快速找到结点;
LinkedList负责调整访问顺序;
Node有key,value。当删除结点,以及map中的key时,Node中的key就派上用场了,代码如下。
LinkedList有删除指定元素的方法remove(Object o);
因为直接使用LinkedList,没有自己去实现一个双向链表,只要熟悉get,put方发的逻辑,实现起来很简单。
get方法:
存在key,删除原来的结点,重新创建一个结点插入到头部。
不存在key,返回-1
put方法:
存在key,删除原来的结点,重新创建一个结点插入到头部。
不存在key,创建一个新结点插入到头部。判断链表的长度是否超过capacity,如果超过,删除尾部的结点。
Node del = li.pollFirst();
map.remove(del.key);
代码:
推荐的写法,参考牛客网编程高频题3——NC93设计LRU缓存结构
import java.util.*;
public class Solution {
public int[] LRU (int[][] operators, int k) {
lru lr = new lru(k);
List<Integer> list = new ArrayList<>();
for(int i = 0; i < operators.length; i++){
if(operators[i][0] == 1){
lr.put(operators[i][1],operators[i][2]);
}
if(operators[i][0] == 2){
int tmp = lr.get(operators[i][1]);
list.add(tmp);
}
}
int[] res = new int[list.size()];
for(int i = 0; i < res.length; i++){
res[i] = list.get(i);
}
return res;
}
}
class lru{
public int capacity;
public HashMap<Integer,Node> map;
public LinkedList<Node> li;
public lru(int size){
capacity = size;
map = new HashMap<>();
li = new LinkedList<>();
}
public int get(int k){
if(map.containsKey(k)){
put(k,map.get(k).value);
return map.get(k).value;
}else{
return -1;
}
}
public void put(int k, int v){
Node newNode = new Node(k,v);
if(map.containsKey(k)){
li.remove(map.get(k));
li.offerLast(newNode);
map.put(k,newNode);
}else{
map.put(k,newNode);
li.offerLast(newNode);
if(map.size() > capacity){
Node del = li.pollFirst();
map.remove(del.key);
}
}
}
}
class Node{
public int key;
public int value;
public Node(int k, int v){
this.key = k;
this.value = v;
}
}
class LRUCache {
class LRUnode{
int key;
int value;
public LRUnode pre;
public LRUnode next;
public LRUnode(){
};
public LRUnode(int _key, int _value){
key = _key;
value = _value;
}
}
int size;
int capacity;
HashMap<Integer,LRUnode> map = new HashMap<>();
public LRUnode head;
public LRUnode tail;
public LRUCache(int capacity) {//有参构造器
this.size = 0;
this.capacity = capacity;
head = new LRUnode();
tail = new LRUnode();
head.next = tail;
tail.pre = head;
}
public int get(int key) {
LRUnode tmp = map.get(key);
if(tmp==null){
return -1;
}
movefirst(tmp);
return tmp.value;
}
public void put(int key, int value) {
LRUnode tmp = map.get(key);
if(tmp != null){
tmp.value = value;
movefirst(tmp);
}else{
size++;
LRUnode newnode = new LRUnode(key, value);
putnode(newnode);
map.put(key,newnode);
if(size > capacity){
size--;
LRUnode del = tail.pre;
delnode(del);
map.remove(del.key);
}
}
}
public void delnode(LRUnode node){
node.pre.next = node.next;
node.next.pre = node.pre;
}
public void putnode(LRUnode node){
node.next = head.next;
head.next.pre = node;
head.next = node;
node.pre = head;
}
public void movefirst(LRUnode node){
/*一定是先删除后增添!否则结点node不能移动到头部。
因为先增添会使node有两个,后删除删除的是刚添加的node,原来node的位置并没有移动*/
delnode(node);
putnode(node);
}
}
题目描述
设计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,输出一个答案
方法一 自行构造LRU数据结构
import java.util.*;
public class Solution {
class LRUnode{
int key;
int value;
LRUnode pre;
LRUnode next;
LRUnode(int k,int v){
this.key = k;
this.value = v;
}
LRUnode(){
}
}
LRUnode head = new LRUnode();
LRUnode tail = new LRUnode();
int size;
int capacity;
HashMap<Integer,LRUnode> map;
Solution(){
}
Solution(int capacity){
this.capacity = capacity;
this.size = 0;
head.next = tail;
tail.pre = head;
this.map = new HashMap<>();
}
public int get(int key){
LRUnode tmp = map.get(key);
if(tmp != null){
movefirst(tmp);
return tmp.value;
}else{
return -1;
}
}
public void put(int key, int value){
LRUnode tmp = map.get(key);
if(tmp != null){
tmp.value = value;
}else{
size++;
LRUnode xin = new LRUnode(key,value);
input(xin);
map.put(key,xin);
if(size > capacity){
map.remove(tail.pre.key);
del(tail.pre);
size--;
}
}
}
public void input(LRUnode node){
head.next.pre = node;
node.next = head.next;
node.pre = head;
head.next = node;
}
public void del(LRUnode node){
node.next.pre = node.pre;
node.pre.next = node.next;
}
public void movefirst(LRUnode node){
del(node);
input(node);
}
public int[] LRU (int[][] operators, int k) {
Solution s = new Solution(k);
ArrayList<Integer> li = new ArrayList<>();
for(int i = 0; i < operators.length; i++){
if(operators[i][0] == 1){
s.put(operators[i][1],operators[i][2]);
}
if(operators[i][0] == 2){
int tmp = s.get(operators[i][1]);
li.add(tmp);
}
}
int[] res = new int[li.size()];
for(int i = 0; i < li.size(); i++){
res[i] = li.get(i);
}
return res;
}
}
方法二 利用LinkedHashMap
题解
- 利用LinkedHashMap来实现每次访问后的数据排在前面。
LinkedHashMap相比于HashMap是有序的,他有两种排序方式,一种是按照插入顺序,另一种是按照访问顺序。LRU需要的是后一种,想要实现访问顺序的排序,new LinkedHashMap()需要输入参数true, false表示按照插入顺序。
- 删除最近最少访问的结点
LinkedHashMap是尾插入,头部是最旧的结点,所以当超过LRU的最大容量时,删除头结点,即:lru.keySet().iterator().next() 。keySet()键集合,iterator()从头结点开始遍历。
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) {
LinkedHashMap<Integer,Integer> lru = new LinkedHashMap<>(k,0.75f,true);
List<Integer> list = new ArrayList<>();
for(int[] ope : operators){
int tmp = ope[0];
int key = ope[1];
if(tmp == 1){
if(lru.containsKey(key)){
lru.remove(key);
}else if(lru.size() >= k){
lru.remove(lru.keySet().iterator().next());
}
lru.put(key,ope[2]);
}else if(tmp == 2){
if(lru.containsKey(key)){
int value = lru.get(key);
lru.remove(key);
lru.put(key,value);
list.add(value);
}else {
list.add(-1);
}
}
}
int[] res = new int[list.size()];
for(int i = 0; i < res.length; i++){
res[i] = list.get(i);
}
return res;
}
}