class LRUCache {
private:
//put和get都算使用
unordered_map<int,int> uTime;
int time;
unordered_map<int,int> uMap;
int capacity;
int num;
public:
LRUCache(int capacity) {
this->capacity=capacity;
this->num=0;
this->time=0;
}
int get(int key) {
if(uMap.find(key)!=uMap.end()){
uTime[key]=time++;
return uMap[key];
}
else return -1;
}
void put(int key, int value) {
//如果关键字已存在变更value就可
if(uMap.find(key)!=uMap.end()){
uMap[key]=value;
uTime[key]=time++;
return;
}
//小于容量直接插入,并且更新成员变量
if(num<capacity){
uMap[key]=value;
uTime[key]=time++;
num++;
}
//如果大于就找到time最小的那个,证明最近没被操作过,删除
else{
int minKey=uTime.begin()->first;
int minTime=uTime.begin()->second;
for(unordered_map<int,int>::iterator it=uTime.begin()++;it!=uTime.end();it++){
if(it->second<minTime){
minKey=it->first;
minTime=it->second;
}
}
uMap.erase(minKey);
uTime.erase(minKey);
uMap[key]=value;
uTime[key]=time++;
num++;
}
}
};
思路说白了就是把使用到的key放到链表头节点
1.get操作算是使用到
2.put操作不管是key存不存在都算一次使用到
所以以上两种操作都会将节点放到头节点
代码实现方面注意点:
1.尽量把所有函数共有的功能提取出来
2.map的key和node的key是同一个key
带泛型Java版本
public class LRUCache {
//双向链表节点
class Node<K,V>{
K key;
V value;
Node<K,V> next;
Node<K,V> pre;
public Node(){
this.next=null;
this.pre=null;
}
public Node(K k,V v){
this.next=null;
this.pre=null;
this.key=k;
this.value=v;
}
}
//哈希表
private Map<Integer,Node<Integer,Integer>> map;
private Integer size;
private Integer capacity;
private Node<Integer,Integer> head;
private Node<Integer,Integer> tail;
//构造函数
public LRUCache(int capacity){
this.map=new HashMap<Integer,Node<Integer,Integer>>();
this.size=0;
this.capacity=capacity;
this.head=new Node<Integer,Integer>();
this.tail=new Node<Integer,Integer>();
this.head.next=this.tail;
this.tail.pre=this.head;
}
private void addToHead(Node<Integer,Integer> node){
head.next.pre=node;
node.next=head.next;
head.next=node;
node.pre=head;
}
private void removeNode(Node<Integer,Integer> node){
node.pre.next=node.next;
node.next.pre=node.pre;
}
private void moveTOHead(Node<Integer,Integer> node){
removeNode(node);
addToHead(node);
}
private Node<Integer,Integer> removeTail(){
Node<Integer, Integer> node = this.tail.pre;
removeNode(node);
return node;
}
public Integer get(Integer key){
Node<Integer,Integer> node = map.get(key);
if(node==null) return -1;
else{
moveTOHead(node);
return node.value;
}
}
public void put(Integer key,Integer value){
Node<Integer, Integer> node = map.get(key);
if(node==null){
Node<Integer, Integer> newNode = new Node<>(key, value);
map.put(key,newNode);
addToHead(newNode);
this.size++;
if(size>capacity){
Node<Integer, Integer> tailNode = removeTail();
map.remove(tailNode.key);
size--;
}
}else{
node.value=value;
moveTOHead(node);
}
}
}
不带泛型Java版本
public class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
}
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode newNode = new DLinkedNode(key, value);
// 添加进哈希表
cache.put(key, newNode);
// 添加至双向链表的头部
addToHead(newNode);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode tail = removeTail();
// 删除哈希表中对应的项
cache.remove(tail.key);
--size;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
手写不带泛型Java版本
问题1:这块不能new一个node放进cache中,因为你这样的话,后面要moveToHead(node),cache中的newNode的指针就没跟着变,就不一样了,导致后面可能就位置错位了,直接改变node的值就可以了
//存在key 更新
//注意点:这块不能new一个node放进cache中,因为你这样的话,后面要moveToHead(node),cache中的newNode的指针就没跟着变,就不一样了,导致后面可能就位置错位了
//cache.put(key, new ListNode(key, value, node.pre, node.next));
node.value = value;
moveToHead(node);
问题2:要注意cache中的node的指针关系要时时刻刻保持和链表中相同,否则get出来就会指针不一致,再去链表中操作,那就会出问题了
package leetcode;
import java.util.*;
class LRUCache {
//public static void main(String[] args) {
// LRUCache lRUCache = new LRUCache(2);
// lRUCache.put(2, 1);
// lRUCache.put(2, 2);
// System.out.println(lRUCache.get(2));
// lRUCache.put(1, 1);
// lRUCache.put(4, 1);
// System.out.println(lRUCache.get(2));
//}
class ListNode {
int key;
int value;
ListNode pre;
ListNode next;
ListNode(int key, int value){
this.key = key;
this.value = value;
this.pre = null;
this.next = null;
}
ListNode(int key, int value, ListNode pre, ListNode next){
this.key = key;
this.value = value;
this.pre = pre;
this.next = next;
}
}
private int capacity;
private int size;
private ListNode DummyHead;
private ListNode DummyTail;
private Map<Integer, ListNode> cache = new HashMap<>();
public LRUCache(int capacity) {
this.capacity = capacity;
this.size = 0;
DummyHead = new ListNode(-1, -1, null, null);
DummyTail = new ListNode(-1, -1, null, null);
DummyHead.next = DummyTail;
DummyTail.pre = DummyHead;
}
public void moveToHead(ListNode node){
deleteNode(node);
insertToHead(node);
}
public void deleteNode(ListNode node){
//断开左右连接
node.next.pre = node.pre;
node.pre.next = node.next;
}
public void insertToHead(ListNode node){
//头插
ListNode t = DummyHead.next;
DummyHead.next = node;
t.pre = node;
//注意点就是改变node的指针
node.next = t;
node.pre = DummyHead;
}
public int get(int key) {
ListNode node = cache.get(key);
if(node==null) return -1;
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
ListNode node = cache.get(key);
if(node!=null){
//存在key 更新
//注意点:这块不能new一个node放进cache中,因为你这样的话,后面要moveToHead(node),cache中的newNode的指针就没跟着变,就不一样了,导致后面可能就位置错位了
//cache.put(key, new ListNode(key, value, node.pre, node.next));
node.value = value;
moveToHead(node);
}else{
//不存在key 插入并且删除过期
ListNode newNode = new ListNode(key, value);
cache.put(key, newNode);
insertToHead(newNode);
this.size++;
//while和if都可以
while(size>0 && size>capacity){
cache.remove(DummyTail.pre.key);
deleteNode(DummyTail.pre);
size--;
}
//if(size>capacity){
// cache.remove(DummyTail.pre.key);
// deleteNode(DummyTail.pre);
// size--;
//}
}
}
}
题解链接
import java.util.*;
public class Solution {
//设置节点结构
static class Node{
int freq;
int key;
int val;
//初始化
public Node(int freq, int key, int val) {
this.freq = freq;
this.key = key;
this.val = val;
}
}
//频率到双向链表的哈希表
private Map<Integer, LinkedList<Node> > freq_mp = new HashMap<>();
//key到节点的哈希表
private Map<Integer, Node> mp = new HashMap<>();
//记录缓存剩余容量
private int size = 0;
//记录当前最小频次
private int min_freq = 0;
public int[] LFU (int[][] operators, int k) {
//构建初始化连接
//链表剩余大小
this.size = k;
//获取操作数
int len = (int)Arrays.stream(operators).filter(x -> x[0] == 2).count();
int[] res = new int[len];
//遍历所有操作
for(int i = 0, j = 0; i < operators.length; i++){
if(operators[i][0] == 1)
//set操作
set(operators[i][1], operators[i][2]);
else
//get操作
res[j++] = get(operators[i][1]);
}
return res;
}
//调用函数时更新频率或者val值
private void update(Node node, int key, int value) {
//找到频率
int freq = node.freq;
//原频率中删除该节点
freq_mp.get(freq).remove(node);
//哈希表中该频率已无节点,直接删除
if(freq_mp.get(freq).isEmpty()){
freq_mp.remove(freq);
//若当前频率为最小,最小频率加1
if(min_freq == freq)
min_freq++;
}
if(!freq_mp.containsKey(freq + 1))
freq_mp.put(freq + 1, new LinkedList<Node>());
//插入频率加一的双向链表表头,链表中对应:freq key value
freq_mp.get(freq + 1).addFirst(new Node(freq + 1, key, value));
mp.put(key, freq_mp.get(freq + 1).getFirst());
}
//set操作函数
private void set(int key, int value) {
//在哈希表中找到key值
if(mp.containsKey(key))
//若是哈希表中有,则更新值与频率
update(mp.get(key), key, value);
else{
//哈希表中没有,即链表中没有
if(size == 0){
//满容量取频率最低且最早的删掉
int oldkey = freq_mp.get(min_freq).getLast().key;
//频率哈希表的链表中删除
freq_mp.get(min_freq).removeLast();
if(freq_mp.get(min_freq).isEmpty())
freq_mp.remove(min_freq);
//链表哈希表中删除
mp.remove(oldkey);
}
//若有空闲则直接加入,容量减1
else
size--;
//最小频率置为1
min_freq = 1;
//在频率为1的双向链表表头插入该键
if(!freq_mp.containsKey(1))
freq_mp.put(1, new LinkedList<Node>());
freq_mp.get(1).addFirst(new Node(1, key, value));
//哈希表key值指向链表中该位置
mp.put(key, freq_mp.get(1).getFirst());
}
}
//get操作函数
private int get(int key) {
int res = -1;
//查找哈希表
if(mp.containsKey(key)){
Node node = mp.get(key);
//根据哈希表直接获取值
res = node.val;
//更新频率
update(node, key, res);
}
return res;
}
}