LRU,最近最少使用算法
//思路
//用哈希表,存key指向数据节点的指针
//双链表存数据节点,最前是最早使用,控制链表长度,通过哈希可以很快找到双链表中的指定的数据节点
// 定义一个双链表,数据节点,保存了数据
struct Node{
int key,value;
Node* prex;
Node* next;
Node():key(0),value(0),prex(nullptr),next(nullptr){}
Node(int a, int b):key(a),value(b),prex(nullptr),next(nullptr){}
};
class LRUCache {
private:
// 链表最大长度和当前容量,链表的头指针,尾指针, 哈希表
int len;
int n;
unordered_map<int, Node*> hashmap;
Node * head;
Node * tail;
public:
LRUCache(int capacity) {
// 初始化指针,长度,头尾节点
len = capacity;
n = 0;
head = new Node();
tail = new Node();
head->next= tail;
tail->prex= head;
}
int get(int key) {
//目标不在缓存,即不在链表中
if(!hashmap.count(key))
return -1;
//在的情况下,返回数据,同时将节点放到链表头,表示最近使用了
Node* oneptr = hashmap[key];
MoveTohead(oneptr);
return oneptr->value;
}
void put(int key, int value) {
// 如果节点不存在,创建新的
if(!hashmap.count(key)){
//创建一个新节点
Node* oneptr = new Node(key, value);
hashmap[key]=oneptr;
//新节点入头
AddTohead(oneptr);
n++;
// 如果超长了,删除尾数据
if(n>len){
Node * temp = deletetail();
//hash中去掉删除的key
hashmap.erase(temp->key);
//防止内存泄漏
delete temp;
--n;
}
}
else{
//否则修改旧的value即可
Node * ptr = hashmap[key];
ptr->value = value;
MoveTohead(ptr);
}
}
// 添加到头部,需要修改头节点的后指针,该节点的前后指针,旧第一个节点的前指针 共4个指针
void AddTohead(Node * node){
node->prex = head;
node->next = head->next;
head->next->prex = node;
head->next = node;
}
// 删除双链表的指定节点,需要修改2个指针,即 前节点的后指针与 后节点的前指针
void RemoveNode(Node* node){
node->prex->next = node->next;
node->next->prex = node->prex;
}
// 使用节点需要做两个事情,将链表的旧位置去掉,添加到链表头位置
void MoveTohead(Node* node){
RemoveNode(node);
AddTohead(node);
}
// 删除尾节点
Node* deletetail(){
Node* temp = tail->prex;
RemoveNode(temp);
return temp;
}
};
/**
* 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);
*/
使用STL现成的结构
struct Node{
int key;
int value;
Node(int a,int b):key(a),value(b){}
};
class LRUCache {
private:
int len;
int n;
// 存放数据节点的双向链表
list<Node> List;
// hash表
unordered_map<int, list<Node>::iterator> hashmap;
public:
LRUCache(int capacity) {
len=capacity;
n=0;
}
int get(int key) {
if(hashmap.find(key) == hashmap.end()){
return -1;
}
List.splice(List.begin(), List, hashmap[key]);
hashmap[key] = List.begin();
return hashmap[key]->value;
}
void put(int key, int value) {
// 如果没有找到这个节点了
if(hashmap.find(key) == hashmap.end()){
// 满了,删最后一个
if(hashmap.size()==len){
hashmap.erase(List.back().key);
List.pop_back();
}
// 加入新的到头部
List.push_front(Node(key, value));
// hash表添加新的节点
hashmap[key] = List.begin();
}
else{
// 存在旧节点,更新value,同时改放到链表头
hashmap[key]->value = value;
List.splice(List.begin(), List, hashmap[key]);
// hash表中也要更新下更新过value的节点
hashmap[key] = List.begin();
}
}
};
/**
* 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);
*/
主要学习到list的splice函数
先看看这splice方法的三种声明:
(1)void splice ( iterator position, list<T,Allocator>& x );
功能: 将 list x 中的元素全都移到position处
(2)void splice ( iterator position, list<T,Allocator>& x, iterator it );
功能: 将 list x 中的由迭代器it指向的元素移到position处
(3)void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last );
功能:将 list x 中的从迭代器 first 到迭代器 last 这一段元素移动到position处
注意:position位置的指定只能是调用者容器中的位置,要剪切的元素是list x中元素
List.splice(List.begin(), List, hashmap[key]);
将List中的hashmap[key]移动到List.begin()的位置。