146. LRU caching mechanism
Use the data structure you master to design and implement a LRU (least recently used) caching mechanism .
Implement LRUCache class:
- LRUCache(int capacity) initializes the LRU cache with a positive integer as capacity
- int get(int key) if the keyword key exists in the cache, the value of the keyword is returned; otherwise, - 1 is returned.
- void put(int key, int value) if the keyword already exists, change its data value; If the keyword does not exist, insert the group keyword value. When the cache capacity reaches the maximum, it should delete the longest unused data value before writing new data, so as to make room for new data values.
Idea: the above figure shows the order in which I open the app, from left to right, from top to bottom. The recently opened app is weather, followed by small black box, microblog and tim. If I switch to tim, tim will be inserted to the front, and all other applications will move back.
A data structure combining linked list and hash table is designed. The Node node of the linked list saves both key and value, while the hash table only saves key and corresponding Node.
- The capacity of LRUCache(int capacity) needs to be saved to prepare for the subsequent put operation to judge whether it exceeds the capacity
- int get(int key). If this keyword exists in the hash table, return the value in the corresponding node, and then move the corresponding node to the list header. Conversely, if it does not exist, it returns - 1
- void put(int key, int value) is equivalent to reopening a new app. At this time, it should be at the top of the list. If the capacity is exceeded at this time, that is, the memory is not enough, you need to delete the application that is hung in the background and has not been used for the longest time, and add a new app.
In this way, the operations of chain header and chain tail are frequently used. The two-way linked list is a very suitable data structure.
code:
struct Node { int key;// Key of linked list int val;// Value of linked list Node* prev;// Pointer to the previous node Node* next;// Pointer to the next node Node(int key, int val) :key(key), val(val), prev(nullptr), next(nullptr) {} }; struct my_list { Node* head; int size; my_list() {//When the linked list is initialized, both prev and next point to the head node to form a ring and form a circular linked list size = 0; head = new Node(-1, -1); head->next = head; head->prev = head; } }; // Add a node prev --- > node --- > next // prev<---node<---next // prev and next here represent a node, and node is the node to be added void _add_node(Node* prev, Node* next, int key, int val) { Node* node = new Node(key, val); node->prev = prev; prev->next = node; node->next = next; next->prev = node; } // Same as the above function void _add_node(Node* prev, Node* next, Node* node) { node->prev = prev; prev->next = node; node->next = next; next->prev = node; } // Add linked list header void add_head(my_list* list, int key, int val) { _add_node(list->head, list->head->next, key, val); list->size++; } // Add end of list void add_tail(my_list* list, int key, int val) { _add_node(list->head->prev, list->head, key, val); list->size++; } // To move a node, first make the next pointer of the previous node point to the next node // Then let the prev pointer of the next node of the node point to the previous node of the node void _move(Node* node) { node->prev->next = node->next; node->next->prev = node->prev; } // Move a node to the end of the linked list void move_to_tail(my_list* list, Node* node) { _move(node); _add_node(list->head->prev, list->head, node); } // Delete nodes in a linked list void delete_node(my_list *list,Node* node) { _move(node); list->size--; } // Delete header node void delete_head(my_list* list) { delete_node(list, list->head->next); } class LRUCache { private: my_list* list; int capacity; unordered_map<int, Node*>umap; public: LRUCache(int capacity):capacity(capacity) { list = new my_list(); } int get(int key) { // If the key in the linked list is a key node, it indicates that the app exists in the application linked list. Open it now // It is at the front of the linked list (here, the tail of the linked list is the front) if (umap.count(key)) { int val = umap[key]->val; move_to_tail(list, umap[key]);// Move to end return val; } return -1; } void put(int key, int value) { // If there is a node with key in the linked list, its value should be updated at this time // Then move it to the front of the linked list if (umap.count(key)) { umap[key]->val = value; move_to_tail(list, umap[key]); return; } // If the capacity is exceeded, delete the last app (the one that has not been used for the longest time), and then delete the newly opened app // Add to front if (list->size >= capacity) { umap.erase(list->head->next->key); delete_head(list); } add_tail(list, key, value); umap[key] = list->head->prev; } };