LeetCode 146 - LRU 缓存
这是一道考哈希表和双向链表的算法题. 题目是这样的
设计并实现一个满足LRU(最近最少使用)缓存约束的数据结构, 实现LRUCache类:



这道算法题有几个点,首先很明显,是key-value结构,我们自然会想到用哈希表来实现。它不需要里面的数据有顺序,也就是说里面数据是无序存储的,所以在C++中我们可以用 unordered_map<key,value>来实现. 题干中要求的添加元素,删除元素,根据key修改value值等,都可以做到。但是有一个难点,就是当插入操作超过了它的最大容量时,需要把最久未被使用的那个键值对从unordered_map中移除,还要保证时间复杂度是O(1) => 这点 unordered_map做不到
这里我们要考虑使用双向链表来实现,用双链表时,我们一般会在链表头部和尾部各增加一个哑节点,也就是说使用一个伪头部(dummy head)和伪尾部(dummy tail)来标记界限. => 这样,添加节点和删除节点就不需要检查相邻的节点是否存在

使用双向链表时,最新活跃的节点我们放在链表的头部,最不活跃的节点我们放在链表的尾部。所以,当插入一个新的节点导致超过了LRUCache的最大容量时,我们需要做两部操作:
1. 把链表尾部的节点删除
2. 把新插入的节点放在双向链表的头部
那么,你可能会问,为什么要使用双向链表呢,单向链表不行么? => 因为单向链表时,我们只能有一个伪头部(dummy head), 要想把链表尾部节点删除,就需要从头开始遍历,一直遍历到某个节点的下一个节点指针是nullptr,这个节点就是尾节点,怎么了,这个时间复杂度显然是O(n), 而不会是O(1) => 所以我们要用双链表
我们使用C++中的无序哈希表 unordered_map, 键就是key, 值就是双链表的节点, 也就是 unordered_map<int, *DLinkedNode>, 双链表的指针定义如下

代码如下
#include <unordered_map>
using namespace std;
struct DLinkNode
{
int key; //这里放key的目的是,当需要删除尾节点时,可以通过key从哈希表unordered_map中删除对应的节点
int value;
DLinkNode* prev;
DLinkNode* next;
DLinkNode() : key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache
{
private:
DLinkNode* dummyHead;
DLinkNode* dummyTail;
int capacity;
int size;
unordered_map<int, DLinkNode*> cache;
public:
LRUCache(int _capacity) : capacity(_capacity),size(0)
{
dummyHead = new DLinkNode();
dummyTail = new DLinkNode();
dummyHead->next = dummyTail;
dummyTail->prev = dummyHead;
}
int GetElement(int key)
{
if(cache.count(key) > 0)
{
DLinkNode* selectedNode = cache[key];
MoveNodeToHead(selectedNode);
return selectedNode->value;
}
return -1;
}
void PutElement(int key, int value)
{
if(cache.count(key) > 0)
{
DLinkNode* currNode = cache[key];
currNode->value = value;
MoveNodeToHead(currNode); //此时这个刚刚更新值的节点变成最近使用的节点,所以移动到链表头部
}
else
{
DLinkNode* newNode = new DLinkNode(key,value);
AddNodeToHead(newNode); //将新节点添加到链表头部,表示最近使用过
cache[key] = newNode;
size++;
if(size > capacity)
{
DLinkNode* tailNode = RemoveTail();
cache.erase(tailNode->key);
delete tailNode; //这里要delete掉尾节点,释放节点占用的内存
tailNode = nullptr; //防止悬空指针
size--;
}
}
}
void MoveNodeToHead(DLinkNode* node)
{
//move the node to the head of the doubly linked list
RemoveNode(node);
AddNodeToHead(node);
}
void AddNodeToHead(DLinkNode* node)
{
dummyHead->next->prev = node;
node->prev = dummyHead;
node->next = dummyHead->next;
dummyHead->next = node;
}
void RemoveNode(DLinkNode* node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
DLinkNode* RemoveTail()
{
if(dummyTail->prev == dummyHead)
{
return nullptr;
}
DLinkNode* tailNode = dummyTail->prev;
RemoveNode(tailNode);
return tailNode;
}
};

浙公网安备 33010602011771号