原文連接:wangwei.one/posts/java-…html
前面,咱們學習了 鏈表 的實現,今天咱們來學習鏈表的一個經典的應用場景——LRU淘汰算法。java
緩存是一種提升數據讀取性能的技術,在硬件設計、軟件開發中都有着很是普遍的應用,好比常見的 CPU 緩存、數據庫緩存、瀏覽器緩存等等。node
緩存的大小有限,當緩存被用滿時,哪些數據應該被清理出去,哪些數據應該被保留?這就須要緩存淘汰策略來決定。常見的策略有三種:先進先出策略 FIFO(First In,First Out)
、最少使用策略 LFU(Least Frequently Used)
、最近最少使用策略 LRU(Least Recently Used)
,本篇將介紹LRU策略算法。git
這一算法的核心思想是,當緩存數據達到預設的上限後,會優先淘汰掉近期最少使用的緩存對象。github
LRU淘汰算法涉及數據的添加與刪除,出於性能考慮,採用鏈表來進行實現,思路以下:算法
null
;
O(n)
,咱們能夠使用散列表HashMap
來記錄每一個Key所對應的Node節點,將時間複雜度降爲O(1)。package one.wangwei.algorithms.utils;
import java.util.HashMap;
import java.util.Map;
/** * LRU Cache * * @author https://wangwei.one * @date 2019/01/29 */
public class LRUCache<K, V> {
private int capacity;
private Node head;
private Node tail;
private Map<K, Node> nodeMap;
public LRUCache(int capacity) {
this.capacity = capacity;
this.nodeMap = new HashMap<>(capacity);
}
/** * Get Key * * @param key * @return */
public V get(K key) {
Node existNode = nodeMap.get(key);
if (existNode == null) {
return null;
}
remove(existNode);
addFirst(existNode);
return existNode.value;
}
/** * Add Key-Value * * @param key * @param value */
public void put(K key, V value) {
Node existNode = nodeMap.get(key);
if (existNode == null) {
Node newNode = new Node(key, value);
if (nodeMap.size() >= capacity) {
removeLast();
}
addFirst(newNode);
}
else {
// update the value
existNode.value = value;
remove(existNode);
addFirst(existNode);
}
}
/** * remove node * * @param node */
private void remove(Node node) {
Node prev = node.prev;
Node next = node.next;
if (prev == null) {
head = next;
} else {
prev.next = next;
}
if (next == null) {
tail = prev;
} else {
next.prev = prev;
}
nodeMap.remove(node.key);
}
/** * add first node * * @param node */
private void addFirst(Node node) {
node.prev = null;
if (head == null) {
head = tail = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
nodeMap.put(node.key, node);
}
/** * remove last */
private void removeLast() {
if (tail == null) {
return;
}
// remove key from map
nodeMap.remove(tail.key);
// remove node from linked list
Node prev = tail.prev;
if (prev != null) {
prev.next = null;
tail = prev;
} else {
head = tail = null;
}
}
private class Node {
private K key;
private V value;
private Node prev;
private Node next;
private Node(K key, V value) {
this.key = key;
this.value = value;
}
}
}
複製代碼
源碼數據庫
LeetCode上相關的練習題:Leetcode 146. LRU Cache瀏覽器
性能測試:LeetCode上運行時間爲88ms
,超過了 43.42%
的Java代碼。緩存