程序员代码面试指南第二版 156.设计LRU缓存结构
题目描述
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
输入描述:
第一行两个个整数N, K,表示操作数量以及缓存结构大小
接下来N行,第一行一个整数opt表示操作类型。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
输出描述:
对于每个操作2,输出一个答案
示例1
输入
6 3
1 1 1
1 2 2
1 3 2
2 1
1 4 4
2 2
输出
1
-1
说明
第一次操作后:最常使用的记录为("1", 1)
第二次操作后:最常使用的记录为("2", 2),("1", 1)变为最不常用的
第三次操作后:最常使用的记录为("3", 2),("1", 1)还是最不常用的
第四次操作后:最常用的记录为("1", 1),("2", 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录("2", 2),加入记录("4", 4),并且为最常使用的记录,然后("3", 2)变为最不常使用的记录
第一次做; 基础数据结构太重要!
import java. util. Scanner;
import java. util. Map;
import java. util. HashMap;
public class Main {
public static void main ( String[ ] args) {
class Node < V> {
int val;
Node last;
Node next;
Node ( int val) {
this . val = val;
}
}
class DoubleLinkedList < V> {
Node< V> head;
Node< V> tail;
public void addNode ( Node< V> node) {
if ( this . head== null) {
this . head = node;
this . tail = node;
}
else {
this . tail. next = node;
node. last = this . tail;
this . tail = node;
}
}
public Node< V> removeHead ( ) {
if ( this . head == null)
return null;
Node< V> curr = this . head;
if ( this . head == this . tail) {
this . head = null;
this . tail = null;
}
else {
this . head. next. last = null;
this . head = this . head. next;
curr. next = null;
}
return curr;
}
public void moveNodeToTail ( Node< V> node) {
if ( node== this . tail)
return ;
if ( node== this . head) {
this . head. next. last = null;
this . head = this . head. next;
}
else {
node. last. next = node. next;
node. next. last = node. last;
}
this . tail. next = node;
node. last = this . tail;
node. next = null;
this . tail = node;
}
}
class LRUCache {
private Map< Integer, Node< Integer> > keyNodeMap;
private Map< Node< Integer> , Integer> nodeKeyMap;
private int capacity;
private DoubleLinkedList< Integer> nodeList;
LRUCache ( int capacity) {
keyNodeMap = new HashMap < > ( ) ;
nodeKeyMap = new HashMap < > ( ) ;
this . capacity = capacity;
nodeList = new DoubleLinkedList < Integer> ( ) ;
}
public void put ( int key, int val) {
if ( keyNodeMap. containsKey ( key) ) {
Node< Integer> curr = keyNodeMap. get ( key) ;
curr. val = val;
nodeList. moveNodeToTail ( curr) ;
}
else {
if ( keyNodeMap. size ( ) == this . capacity) {
Node< Integer> head = nodeList. removeHead ( ) ;
Integer keyTBD = nodeKeyMap. get ( head) ;
nodeKeyMap. remove ( head) ;
keyNodeMap. remove ( keyTBD) ;
}
Node< Integer> node = new Node < > ( val) ;
keyNodeMap. put ( key, node) ;
nodeKeyMap. put ( node, key) ;
nodeList. addNode ( node) ;
}
}
public int get ( Integer key) {
if ( keyNodeMap. containsKey ( key) ) {
Node< Integer> curr = keyNodeMap. get ( key) ;
nodeList. moveNodeToTail ( curr) ;
return curr. val;
}
else {
return - 1 ;
}
}
}
Scanner sc = new Scanner ( System. in) ;
String s = sc. nextLine ( ) ;
int n = Integer. parseInt ( s. split ( " " ) [ 0 ] ) ;
int k = Integer. parseInt ( s. split ( " " ) [ 1 ] ) ;
LRUCache cache = new LRUCache ( k) ;
for ( int i= 0 ; i< n; i++ ) {
s = sc. nextLine ( ) ;
int opt = Integer. parseInt ( s. split ( " " ) [ 0 ] ) ;
switch ( opt) {
case 1 :
int key = Integer. parseInt ( s. split ( " " ) [ 1 ] ) ;
int val = Integer. parseInt ( s. split ( " " ) [ 2 ] ) ;
cache. put ( key, val) ;
break ;
case 2 :
int key2 = Integer. parseInt ( s. split ( " " ) [ 1 ] ) ;
int res = cache. get ( key2) ;
System. out. println ( res) ;
break ;
}
}
}
}