题解 | #设计LRU缓存结构#
设计LRU缓存结构
https://www.nowcoder.com/practice/5dfded165916435d9defb053c63f1e84
基本思想
需要O(1)时间的插入删除,必须要一个map,此外需要判断时间,那么还需要一个双链表。如果自己实现双链表,会很麻烦,可以直接使用list。
需要设置一个节点类型,Node{k,v},list存储Node*指针。注意必须要有k,方便后续删除最后一个节点的时候,根据k去删除map。
接下来就是map了,注意这里的value需要指向list中的元素的位置,因此不能是Node*,这样会没法直接定位到链表的节点,需要 list<Node*>::iterator 存储一个迭代器,这样*解引用就可以指向那个被访问的list节点了,方便插入和删除。
核心结构如下
struct Node { int k, v; }; map<int, list<Node*>::iterator> kv; list<Node*> li;
代码演示
代码很详细,按照我的注释逻辑,手动推演一下,很容易明白。
struct Node { int k, v; }; struct Solution { map<int, list<Node*>::iterator> kv; // O1查找 list<Node*> li; // 区分前后,begin() 最新, int capacity ; Solution(int c): capacity(c){ } ~Solution() { for (Node* pn : li) { delete pn; } } void set(int k, int v) { // 如果存在 更新即可 if (kv.count(k)) { auto node = kv[k]; (*node)->v = v; // 把迭代器解引用,然后取出v get(k); // update } else { // 否则插入 Node* cur=new Node{ k,v }; li.push_front(cur); //前面是最新的 kv[k] = li.begin(); //map存储的是迭代器 if (kv.size() > capacity) { // 删除多余的 Node* del = li.back(); kv.erase(del->k); // map和list中同时删除 li.pop_back(); delete del; //记得别内存泄露了 } } } int get(int k) { int ret = -1; // 如果存在 if (kv.count(k)) { auto cur = kv[k]; ret = (*cur)->v; // 更新位置 li.push_front(*cur); // 这两步别反了,先把Node*插入头,再反过头删除原来的迭代器位置 li.erase(cur); kv[k] = li.begin(); // 重新指向新的迭代器 return ret; } else { //否则 返回-1 return ret; } } };