问题链接:https://leetcode.com/problems/lru-cache/
该问题可以具体参考算法设计这一篇,采用泛型编程完成,本题代码如下:
class LRUCache {
private HashMap<Integer,DLinkedListNode> hashmap;
private int capacity; //缓存容量
private DLinkedListNode head; //双端链表头结点
private DLinkedListNode tail; //双端链表尾节点
private class DLinkedListNode
{
int key;
int val;
DLinkedListNode pre;
DLinkedListNode next;
public DLinkedListNode(int key,int val)
{
this.key=key;
this.val=val;
this.pre=null;
this.next=null;
}
}
public LRUCache(int capacity) {
this.hashmap=new HashMap<>();
this.capacity=capacity;
this.head=null;
this.tail=null;
}
public int get(int key) {
DLinkedListNode node= hashmap.get(key);
if(node==null) //当hashmap中不存在这个key时,返回null
return -1;
if(node!=this.tail) //存在这个key时,检查是否是尾节点:如果是尾节点,则该节点位置不动;如果不是尾节点,则需要移动位置。
{
if(node==this.head) //如果是头结点,则将头结点放在双端链表尾部
head=head.next;
else //如果不是头节点,则将该节点从双端链表中移到尾部
{
node.pre.next=node.next;
node.next.pre=node.pre;
}
tail.next=node;
node.pre=tail;
node.next=null;
tail=node;
}
return node.val;
}
public void put(int key, int value) {
DLinkedListNode node=hashmap.get(key);
if(node!=null)
{
node.val=value;
if(node==this.head)
{
if(this.hashmap.size()==1)
return;
head=head.next;
}
else if(node==this.tail)
return;
else
{
node.pre.next=node.next;
node.next.pre=node.pre;
}
tail.next=node;
node.pre=tail;
node.next=null;
tail=node;
}
else
{
DLinkedListNode newNode=new DLinkedListNode(key,value);
if(this.hashmap.size()==this.capacity)
{
DLinkedListNode tmp=this.head;
this.head=this.head.next;
this.hashmap.remove(tmp.key);
}
if(head==null && tail==null)
{
this.head = newNode;
this.tail = newNode;
}
else
{
this.tail.next=newNode;
newNode.pre=this.tail;
newNode.next=null;
}
this.tail=newNode;
this.hashmap.put(key,newNode);
}
}
}
/**
* 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);
*/
上述算法在理论上已经达到时间和空间上的平衡最优解,但看效率最高的算法的思路与之一致,代码如下,只是在细节处理时抽象的更好——访问或者修改双端链表中的值时移动对应节点的操作,这一步抽象的十分简洁:
class LRUCache {
private class Node {
int key;
int val;
Node prev;
Node next;
Node( int key, int val ) {
this.key = key;
this.val = val;
}
}
private HashMap<Integer, Node> keys = new HashMap<>();
private Node head = null;
private Node tail = null;
private int capacity = 0;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
Node node = keys.get( key );
if( node != null ) {
nodeTouch( node );
return( node.val );
}
return( -1 );
}
public void put(int key, int value) {
Node node = keys.get( key );
if( node != null ) {
node.val = value;
nodeTouch( node );
} else {
if( keys.size() == capacity ) {
keys.remove( tail.key );
nodeRemove( tail );
}
nodeAdd( new Node( key, value ));
keys.put( key, head );
}
}
private void nodeTouch( Node node ) {
nodeRemove( node );
nodeAdd( node );
}
private void nodeAdd( Node node ) {
node.prev = null;
node.next = head;
if( head != null ) {
head.prev = node;
} else {
tail = node;
}
head = node;
}
private void nodeRemove( Node node ) {
if( node.prev != null ) {
node.prev.next = node.next;
} else {
head = node.next;
if( head != null ) {
head.prev = null;
} else {
tail = null;
}
}
if( node.next != null ) {
node.next.prev = node.prev;
} else {
tail = node.prev;
if( tail != null ) {
tail.next = null;
} else {
head = null;
}
}
}
}
/**
* 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);
*/