力扣146;核心结构是双向链表+哈希表实现(插入和查找复杂度均为O(1))
思路是:
维护一个双向链表(使得插入和删除节点时间为常数),每次插入或者查询的节点放在链表头部;
最长时间未使用的放在尾部;链表长度不得超过限制;链表中所有节点存放在
unordered_map中(使得查询时间为常数);
(1)插入节点时,先看节点是否在链表(哈希表)中, 若在,直接更新值。
先检查链表是否达到限制长度:若达到,则删除尾节点,并从哈希表中移除,再插入新建节点;若未达到,则直接插入;
(2)查询节点时,则哈希表直接查询;但之后将此节点移至链表头(其中涉及到删除节点,添加节点;但无需从哈希表中删除)
struct node{
int key;
int value;
node* pre;
node* next;
node(int k, int v) : key(k), value(v), pre(NULL), next(NULL){}
};
class LRUCache {
public:
LRUCache(int capacity) {
maxSize = capacity;
beforeHead_ = new BiLisNode(-1, -1);
afterTail_ = new BiLisNode(-1, -1);
beforeHead_->next = afterTail_;
afterTail_->prev = beforeHead_;
}
public:
void set(int key, int value)
{
//找到则更新
if(mp.find(key) != mp.end()){
mp[key]->value = value;
node* tmp = mp[key];
removeNode(tmp);
addNode(tmp);
return;
}
//没找到则新建
node* tmp = new node(key, value);
//判断是否达到大小限制
if(mp.size() >= maxSize){
//达到则删除尾节点
mp.erase(m_afterTail->pre->key);
removeNode(m_afterTail->pre);
}
//在头部插入新节点,并加入哈希表
addNode(tmp);
mp[key] = tmp;
}
int get(int key)
{
if(mp.find(key) == mp.end())
return -1;
node* tmp = mp[key];
removeNode(tmp);
addNode(tmp);
return tmp->value;
}
//不包含移除出哈希表操作;因为在get操作中有删除操作,但又有增加操作
void removeNode(node* x)
{
if(x != NULL){
x->pre->next = x->next;
x->next->pre = x->pre;
}
}
//添加节点到头部
void addNode(node* x)
{
if(x != NULL){
x->pre = m_beforeHead;
x->next = m_beforeHead->next;
m_beforeHead->next->pre = x;
m_beforeHead->next = x;
}
}
private:
unordered_map<int, node*> mp;
node* m_beforeHead;
node* m_afterTail;
int maxSize;
};
代码实现有一些小技巧:
1.使用额外的头前节点和尾后节点(便于插入头结点和删除尾节点,不用做额外的判断)
2.将key,value都存储在节点中,再通过key做映射,这样,在删除节点时,不用通过在map中找到对应的value再删除(O(N)复杂度)
之前的做法:只是将value存储在节点中,然后key与节点做映射,删除节点时,遍历了map找到value等于此节点的key,再erase