題意:本題的題目較難讀懂,不過這能考察一個面試者能否短時間內,理解一段未見過的內容的能力。在題目的wiki鏈接裏提供了這樣一張圖:
緩存的長度(capacity )爲4,A、B、C、D首先插入緩存中;插入E時,超過了capacity ,需要覆蓋最近最少使用的A;插入F時,超過了capacity ,需要覆蓋B。
爲最近最少使用(LRU)的緩存策略設計一個數據結構,它應該支持以下操作:獲取數據(get)和寫入數據(put)。
1.獲取數據get(key):如果緩存中存在key,則獲取其數據值(通常是正數),否則返回-1。
2.寫入數據put(key, value):如果key還沒有在緩存中,則寫入其數據值。當緩存達到上限,它應該在寫入新數據之前刪除最近最少使用的數據用來騰出空閒位置。
要求:時間複雜度爲O(1)。
例子:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2(此時最近最少使用是2,因爲1被get操作過)
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
分析:
要求時間複雜度爲O(1),因此很容易聯想到哈希表,同時題目中沒有給定節點的結構,所以我們可以定義一個雙向鏈表,這樣方便我們查找。同時使用哈希表和雙向鏈表才能保證常數的時間複雜度。
哈希表跟蹤雙鏈表中的鍵和值,即key爲node的值,value爲node。爲了使代碼更加整潔,需要創建了一個僞頭和僞尾的標記邊界,所以我們不需要在更新過程中檢查NULL節點。
具體到get和put方法並不是很難,關鍵是把存取的過程想清楚就不難寫出代碼。
import java.util.Hashtable;
class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode pre;
DLinkedNode post;
}
/**
* Always add the new node right after head;
*/
private void addNode(DLinkedNode node){
node.pre = head;
node.post = head.post;
head.post.pre = node;
head.post = node;
}
/**
* Remove an existing node from the linked list.
*/
private void removeNode(DLinkedNode node){
DLinkedNode pre = node.pre;
DLinkedNode post = node.post;
pre.post = post;
post.pre = pre;
}
/**
* Move certain node in between to the head.
*/
private void moveToHead(DLinkedNode node){
this.removeNode(node);
this.addNode(node);
}
// pop the current tail.
private DLinkedNode popTail(){
DLinkedNode res = tail.pre;
this.removeNode(res);
return res;
}
private Hashtable<Integer, DLinkedNode>
cache = new Hashtable<Integer, DLinkedNode>();
private int count;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.count = 0;
this.capacity = capacity;
head = new DLinkedNode();
head.pre = null;
tail = new DLinkedNode();
tail.post = null;
head.post = tail;
tail.pre = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if(node == null){
return -1; // should raise exception here.
}
// move the accessed node to the head;
this.moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if(node == null){
DLinkedNode newNode = new DLinkedNode();
newNode.key = key;
newNode.value = value;
this.cache.put(key, newNode);
this.addNode(newNode);
++count;
if(count > capacity){
// pop the tail
DLinkedNode tail = this.popTail();
this.cache.remove(tail.key);
--count;
}
}else{
// update the value.
node.value = value;
this.moveToHead(node);
}
}
}
/**
* 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);
*/