LRU缓存
思路
了解LRU(最近最少使用)缓存机制的原理之后,主要还是当容量达到上限时如何才能删除掉最久未使用的数据,可以考虑使用LinkedHashMap吧,可以维持插入的顺序,如果需要删除掉最久未使用的数据,那么可以使用迭代器迭代删掉第一个数据即可。(这里默认最近最少未被使用的项就是按插入顺序最先插入的项)
class LRUCache {
private int cap;
private Map<Integer, Integer> map = new LinkedHashMap<>(); // 保持插入顺序
public LRUCache(int capacity) {
this.cap = capacity;
}
public int get(int key) {
if (map.keySet().contains(key)) {
int value = map.get(key);
map.remove(key);
// 保证每次查询后,都把数据放在末尾
map.put(key, value);
return value;
}
return -1;
}
public void put(int key, int value) {
if (map.keySet().contains(key)) {//如果存在就需要更新顺序
map.remove(key);
} else if (map.size() == cap) {
Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
iterator.next();//找到最早插入未被使用过的数据
iterator.remove();
}
map.put(key, value);
}
}
/**
* 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);
*/
LFU缓存
思路
在这里跟LRU缓存实现还是有一些区别,这里的最不经常使用时当达到容量上限时,要删除使用频次最少的项,如果存在相同的最少频次,就需要删除最久未使用的项,所以可以需要存储缓存的插入顺序,同时还要记录各项的频次以及缓存的内容,单纯只使用一条双向链表的话应该没法在O(1)的时间复杂度实现了,还得多拿一些空间。
class LFUCache {
Map<Integer, Node> cache; // 存储缓存的内容
Map<Integer, LinkedHashSet<Node>> freqMap; // 存储每个频次对应的双向链表
int size;
int capacity;
int min; // 存储当前最小频次
public LFUCache(int capacity) {
cache = new HashMap<> (capacity);
freqMap = new HashMap<>();
this.capacity = capacity;
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) {
return -1;
}
freqInc(node);
return node.value;
}
public void put(int key, int value) {
if (capacity == 0) {
return;
}
Node node = cache.get(key);
if (node != null) {
node.value = value;
freqInc(node);
} else {
if (size == capacity) {
Node deadNode = removeNode();
cache.remove(deadNode.key);
size--;
}
Node newNode = new Node(key, value);
cache.put(key, newNode);
addNode(newNode);
size++;
}
}
void freqInc(Node node) {
// 从原freq对应的链表里移除, 并更新min
int freq = node.freq;
LinkedHashSet<Node> set = freqMap.get(freq);
set.remove(node);
if (freq == min && set.size() == 0) {
min = freq + 1;
}
// 加入新freq对应的链表
node.freq++;
LinkedHashSet<Node> newSet = freqMap.get(freq + 1);
if (newSet == null) {
newSet = new LinkedHashSet<>();
freqMap.put(freq + 1, newSet);
}
newSet.add(node);
}
void addNode(Node node) {
LinkedHashSet<Node> set = freqMap.get(1);
if (set == null) {
set = new LinkedHashSet<>();
freqMap.put(1, set);
}
set.add(node);
min = 1;
}
Node removeNode() {
LinkedHashSet<Node> set = freqMap.get(min);
Node deadNode = set.iterator().next();
set.remove(deadNode);
return deadNode;
}
}
class Node {
int key;
int value;
int freq = 1;
public Node() {}
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
在这里贴一个甜姨的实现,自己写了底层实现代码,所以看着特别长,但是效率特别高哦hhh,有兴趣了解的可以关注甜姨的wx公众号【甜姨的奇妙冒险】或知乎专栏【甜姨的力扣解题】!
class LFUCache {
Map<Integer, Node> cache;
DoublyLinkedList firstLinkedList;
DoublyLinkedList lastLinkedList;
int size;
int capacity;
public LFUCache(int capacity) {
cache = new HashMap<> (capacity);
firstLinkedList = new DoublyLinkedList();
lastLinkedList = new DoublyLinkedList();
firstLinkedList.post = lastLinkedList;
lastLinkedList.pre = firstLinkedList;
this.capacity = capacity;
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) {
return -1;
}
freqInc(node);
return node.value;
}
public void put(int key, int value) {
if (capacity == 0) {
return;
}
Node node = cache.get(key);
if (node != null) {
node.value = value;
freqInc(node);
} else {
if (size == capacity) {
// 如果缓存满了,删除lastLinkedList.pre链表中的tail.pre的Node,如果该链表中的元素删空了,则删掉该链表
cache.remove(lastLinkedList.pre.tail.pre.key);
lastLinkedList.removeNode(lastLinkedList.pre.tail.pre);
size--;
if (lastLinkedList.pre.head.post == lastLinkedList.pre.tail) {
removeDoublyLinkedList(lastLinkedList.pre);
}
}
Node newNode = new Node(key, value);
cache.put(key, newNode);
if (lastLinkedList.pre.freq != 1) {
DoublyLinkedList newDoublyLinedList = new DoublyLinkedList(1);
addDoublyLinkedList(newDoublyLinedList, lastLinkedList.pre);
newDoublyLinedList.addNode(newNode);
} else {
lastLinkedList.pre.addNode(newNode);
}
size++;
}
}
void freqInc(Node node) {
// 将node从原freq对应的链表里移除, 如果链表空了则删除链表,
DoublyLinkedList linkedList = node.doublyLinkedList;
DoublyLinkedList preLinkedList = linkedList.pre;
linkedList.removeNode(node);
if (linkedList.head.post == linkedList.tail) {
removeDoublyLinkedList(linkedList);
}
// 将node加入新freq对应的链表,若该链表不存在,则先创建该链表。
node.freq++;
if (preLinkedList.freq != node.freq) {
DoublyLinkedList newDoublyLinedList = new DoublyLinkedList(node.freq);
addDoublyLinkedList(newDoublyLinedList, preLinkedList);
newDoublyLinedList.addNode(node);
} else {
preLinkedList.addNode(node);
}
}
void addDoublyLinkedList(DoublyLinkedList newDoublyLinedList, DoublyLinkedList preLinkedList) {
newDoublyLinedList.post = preLinkedList.post;
newDoublyLinedList.post.pre = newDoublyLinedList;
newDoublyLinedList.pre = preLinkedList;
preLinkedList.post = newDoublyLinedList;
}
void removeDoublyLinkedList(DoublyLinkedList doublyLinkedList) {
doublyLinkedList.pre.post = doublyLinkedList.post;
doublyLinkedList.post.pre = doublyLinkedList.pre;
}
}
class Node {
int key;
int value;
int freq = 1;
Node pre;
Node post;
DoublyLinkedList doublyLinkedList;
public Node() {}
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
class DoublyLinkedList {
int freq;
DoublyLinkedList pre;
DoublyLinkedList post;
Node head;
Node tail;
public DoublyLinkedList() {
head = new Node();
tail = new Node();
head.post = tail;
tail.pre = head;
}
public DoublyLinkedList(int freq) {
head = new Node();
tail = new Node();
head.post = tail;
tail.pre = head;
this.freq = freq;
}
void removeNode(Node node) {
node.pre.post = node.post;
node.post.pre = node.pre;
}
void addNode(Node node) {
node.post = head.post;
head.post.pre = node;
head.post = node;
node.pre = head;
node.doublyLinkedList = this;
}
}
参考资料:甜姨的N种题解···