LeetCode 146. LRU 缓存机制
思路
哈希表 + 双向链表。在面试中,面试官一般会期望读者能够自己实现一个简单的双向链表,而不是使用语言自带的、封装好的数据结构。
方法一:哈希表 + C++自带的双向链表 list
1 struct Node { 2 int key; 3 int value; 4 Node(){} 5 Node(int _key,int _value): key(_key), value(_value){} 6 }; 7 8 class LRUCache { 9 private: 10 int capacity; 11 list<Node*> cache; 12 unordered_map<int, list<Node*>::iterator> mp; 13 public: 14 LRUCache(int capacity) { 15 this->capacity = capacity; 16 } 17 18 int get(int key) { 19 if(mp.find(key) != mp.end()) { 20 //将其更新到队头 21 Node* node = *(mp[key]); 22 cache.erase(mp[key]); 23 cache.push_front(node); 24 mp[key] = cache.begin(); 25 26 //返回对应的值 27 return node->value; 28 } else { 29 return -1; 30 } 31 } 32 33 void put(int key, int value) { 34 if(mp.find(key) != mp.end()) { 35 Node* node = *(mp[key]); 36 cache.erase(mp[key]); 37 node->value = value; 38 cache.push_front(node); 39 mp[key] = cache.begin(); 40 } else { 41 Node* node = new Node(key, value); 42 cache.push_front(node); 43 mp[key] = cache.begin(); 44 if(cache.size() > capacity) { 45 mp.erase(cache.back()->key); 46 cache.pop_back(); 47 } 48 } 49 } 50 }; 51 52 /** 53 * Your LRUCache object will be instantiated and called as such: 54 * LRUCache* obj = new LRUCache(capacity); 55 * int param_1 = obj->get(key); 56 * obj->put(key,value); 57 */
复杂度分析
时间复杂度:get和put都是O(1)。
方法二:哈希表 + 自己实现的简单双向链表
1 struct DLinkedNode { 2 int key, value; 3 DLinkedNode* prev; 4 DLinkedNode* next; 5 DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {} 6 DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {} 7 }; 8 9 class LRUCache { 10 private: 11 unordered_map<int, DLinkedNode*> cache; 12 DLinkedNode* head; 13 DLinkedNode* tail; 14 int size; 15 int capacity; 16 17 public: 18 LRUCache(int _capacity): capacity(_capacity), size(0) { 19 // 使用伪头部和伪尾部节点 20 head = new DLinkedNode(); 21 tail = new DLinkedNode(); 22 head->next = tail; 23 tail->prev = head; 24 } 25 26 int get(int key) { 27 if (!cache.count(key)) { 28 return -1; 29 } 30 // 如果 key 存在,先通过哈希表定位,再移到头部 31 DLinkedNode* node = cache[key]; 32 moveToHead(node); 33 return node->value; 34 } 35 36 void put(int key, int value) { 37 if (!cache.count(key)) { 38 // 如果 key 不存在,创建一个新的节点 39 DLinkedNode* node = new DLinkedNode(key, value); 40 // 添加进哈希表 41 cache[key] = node; 42 // 添加至双向链表的头部 43 addToHead(node); 44 ++size; 45 if (size > capacity) { 46 // 如果超出容量,删除双向链表的尾部节点 47 DLinkedNode* removed = removeTail(); 48 // 删除哈希表中对应的项 49 cache.erase(removed->key); 50 // 防止内存泄漏 51 delete removed; 52 --size; 53 } 54 } 55 else { 56 // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部 57 DLinkedNode* node = cache[key]; 58 node->value = value; 59 moveToHead(node); 60 } 61 } 62 63 void addToHead(DLinkedNode* node) { 64 node->prev = head; 65 node->next = head->next; 66 head->next->prev = node; 67 head->next = node; 68 } 69 70 void removeNode(DLinkedNode* node) { 71 node->prev->next = node->next; 72 node->next->prev = node->prev; 73 } 74 75 void moveToHead(DLinkedNode* node) { 76 removeNode(node); 77 addToHead(node); 78 } 79 80 DLinkedNode* removeTail() { 81 DLinkedNode* node = tail->prev; 82 removeNode(node); 83 return node; 84 } 85 };
复杂度分析
时间复杂度:对于 put 和 get 都是 O(1)。
空间复杂度:O(capacity),因为哈希表和双向链表最多存储 capacity + 1个元素。