Idea:
This is equivalent to building a doubly linked list. The value part of the linked list is a hash table. Its function is to use the hash table to quickly find the specified node of the linked list.
(1) Build a linked list
class DLinkNode {
public:
int key, value;
DLinkNode* prev, * next;
DLinkNode() : key(0), value(0), prev(nullptr), next(nullptr) {
}
DLinkNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {
}
};
(2) The initialization parameter
uses capacity to save the capacity, and size to control the capacity after each addition. head and tail are used to control the head and tail of the linked list, head always points to the head, and tail always points to the end.
int size, capacity;
DLinkNode* head, * tail;
unordered_map<int, DLinkNode*> hash;
(3) Initialize the linked list
LRUCache(int capacity) :
size(0), capacity(capacity)
{
// 初始化双向链表
head = new DLinkNode();
tail = new DLinkNode();
head->next = tail;
tail->prev = head;
}
(4) Two operations
The first operation is to get a key and return its value. The second operation is put, which modifies the value of the corresponding key. If it does not exist, create a new one. If it exceeds the upper limit, remove the first one (the one with the lowest usage rate). So we have the following operations on the linked list
① delete a node at a certain position. void deleteNode(int key)
② Add a node at the end. void add2Tail(int key)
③When the upper limit is exceeded, delete the first node. void deleteHead()
When operating, first understand the logic:
get(): Determine whether this value already exists, if so, return the corresponding value, and move this node to the end.
int get(int key) {
// 若不存在 key
if (hash.count(key) == 0) {
return -1;
}
move2Tail(key); // 最近访问的 key 挪到链尾
return hash[key]->value;
}
put(): If there is a key, modify the value and move at the same time, which is similar to put here. If it does not exist, create a new node and insert the end of the chain, size++. If the size exceeds the capacity, delete the chain head node.
void put(int key, int value) {
// 若已存在 key
if (hash.count(key) != 0) {
// 修改已有 key 所对应的 value
hash[key]->value = value;
move2Tail(key); // 挪到链尾
return;
}
// 若不存在则 new
DLinkNode* newNode = new DLinkNode(key, value);
hash[key] = newNode;
// 插入链尾
add2Tail(key);
size++;
// 若超出规定容量
if (size > capacity) {
deleteHead();
size--;
}
}
(5) Operate the linked list
void deleteNode(int key) {
// key 的前驱 next 指向 key 的后继
hash[key]->prev->next = hash[key]->next;
// key 的后继 prev 指向 key 的前驱
hash[key]->next->prev = hash[key]->prev;
}
void add2Tail(int key) {
// tail 的前驱 next 指向 key
tail->prev->next = hash[key];
// key 的 prev 指向 tail 的前驱
hash[key]->prev = tail->prev;
// tail 的 prev 指向 key
tail->prev = hash[key];
// key 的 next 指向 tail
hash[key]->next = tail;
}
void move2Tail(int key) {
deleteNode(key);
add2Tail(key);
}
void deleteHead() {
DLinkNode* deleted = head->next;
// head 的 next 指向 head 的后继 next
head->next = deleted->next;
// head 后继的 prev 指向 head
deleted->next->prev = head;
// 同时也从 hash 中注销
hash.erase(deleted->key);
delete deleted;
}