1. 什么是LRU算法
LRU是Least Recently Used的縮寫,即最近最久未使用,常用於頁面置換算法,是為虛擬頁式存儲管理服務的。
LRU算法的提出,是基於這樣一個事實:在前面幾條指令中使用頻繁的頁面很可能在后面的幾條指令中頻繁使用。反過來說,已經很久沒有使用的頁面很可能在未來較長的一段時間內不會被用到。
設計並實現了一個最近最少使用(LRU)緩存的數據結構,它應該支持以下操作:get和put。
get(key)——如果鍵存在於緩存中,則獲得鍵值(總是正數),否則返回-1。
put(key value)——如果鍵不存在,則設置或插入值。當緩存達到其容量時,應在插入新項之前使最近最少使用的項無效。
LRU緩存可使用HashMap和雙向鏈表實現。HashMap使得get的時間是O(1);雙向鏈表使節點添加/刪除操作O(1)。
2. Java實現LRU算法
數據結構:
/**
* 定義一個雙向鏈表結構
*/
public class Node {
int key;
int value;
// 上一個節點
Node pre;
// 下一個節點
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
import java.util.HashMap;
/**
* 緩存類
*/
public class LRUCache {
int capacity;
HashMap<Integer, Node> map = new HashMap<Integer, Node>();
Node head = null;
Node tail = null;
public LRUCache(int capacity) {
// 容器容量
this.capacity = capacity;
}
public int get(int key) {
// 判斷key是否已經存在
if (map.containsKey(key)) {
// 存在取出節點
Node n = map.get(key);
// 刪除節點在雙向鏈表中的位置
remove(n);
// 將當前節點設置為頭節點
setHead(n);
return n.value;
}
return -1;
}
/**
* 刪除節點在雙向鏈表中的位置
* @param n
*/
public void remove(Node n) {
if (n.pre != null) {
// 將n的上一個節點的下一個節點指向n的下一個節點
n.pre.next = n.next;
} else {
// 如果n的上一個節點為null,說明n是頭節點,將n的下一個節點設置為頭節點
head = n.next;
}
if (n.next != null) {
// 將n的下一個節點的上一個節點指向n的上一個節點
n.next.pre = n.pre;
} else {
// 如果n的下一個節點為null,說明n是尾節點,將n的上一個節點設置為尾節點
tail = n.pre;
}
}
/**
* 將n設置為頭節點
* @param n
*/
public void setHead(Node n) {
// 將n的下一個節點指向現在的頭節點
n.next = head;
// 將n的上一個節點指向null,因為n要為頭節點
n.pre = null;
// 如果之前頭節點不為空,則將之前頭結點的上一個節點指向n
if (head != null)
head.pre = n;
// 將頭節點設置為n
head = n;
// 如果尾節點為空,說明只有一個元素,把尾節點設置為和頭節點一樣的元素
if (tail == null)
tail = head;
}
/**
* 取出元素
* @param key
* @param value
*/
public void put(int key, int value) {
// 判斷key是否已經存在
if (map.containsKey(key)) {
// 存在,取出老節點
Node old = map.get(key);
// 將老節點值設置為最新值
old.value = value;
// 刪除老節點在雙向鏈接中的位置
remove(old);
// 將老節點設置為頭節點
setHead(old);
} else {
// 創建一個節點
Node created = new Node(key, value);
// 判斷當前map容器是否超過最大值
if (map.size() >= capacity) {
// 超過最大值則刪除(通過雙向鏈表中尾節點記錄的key進行刪除)
map.remove(tail.key);
// 刪除雙向鏈表尾節點元素
remove(tail);
// 把當前節點設置為頭節點
setHead(created);
} else {
// 把當前節點設置為頭節點
setHead(created);
}
// 存入map中
map.put(key, created);
}
}
}