题目:LRU 缓存
使用到的是哈希表和链表(重点是链表迭代器)
这题难点在于“逐出最久未使用的关键字”,首先明确思路:既然要求常数时间的复杂度,那么就一定会要用到哈希表(查找),但是又要求有一个数据结构可以存储元素使用的顺序,并且改变顺序时也要是O(1)的时间复杂度,那么就也要使用到链表(通过调用 Splice函数 可以改变迭代器位置,从而在常数时间内实现顺序的改变)
但是如何将哈希表与链表结合起来?答:将value和iterator绑定在一起,这样就通过key就既可以得到value的值,又可以实现iterator的移动
具体实现:
首先拿到最大容量,避免后续多次函数调用产生的开销;
然后开始定义 get 函数:
- 如果哈希表内能够找到对应的key,那么就认为这个元素被访问了一次,因此也就需要将其放在链表的尾部,这里可以通过key在哈希表中找到对应的iterator
- splice()函数的语法之一(移动单个节点):
* **void splice(const_iterator pos, list& other, const_iterator it);**
- 将 `other` 链表中由 `it` 指向的那个元素剪切到 `this` 链表的 `pos` 位置
最后实现 put 函数:
- 这里有两种情况需要处理:1.当前key已经存在 2.容量已满
- 1.key重复:
* 意味着需要覆写之前的value,将其移至list尾部,这里还是使用到splice()
- 2.容量已满:
* 此时需要删除头节点,直接pop_front()即可;同时也要同步删除哈希表中对应的元素
最后将新插入的元素添加到两个容器即可
1class LRUCache
2{
3public:
4 LRUCache(int capacity) : capacity(capacity)
5 {
6 cache.reserve(capacity);
7 }
8
9 int get(int key)
10 {
11 if(cache.contains(key))
12 {
13 auto it = cache.find(key);
14 order.splice(order.end(), order, it->second.second);
15 return it->second.first;
16 }
17 else return -1;
18 }
19
20 void put(int key, int value)
21 {
22 auto it = cache.find(key);
23 if(it != cache.end()) // 如果value已经存在,则更新
24 {
25 it->second.first = value;
26 order.splice(order.end(), order, it->second.second);
27 return;
28 }
29 if(cache.size() >= capacity) // 处理超出容量情况
30 {
31 cache.erase(order.front());
32 order.pop_front();
33 }
34
35 order.push_back(key);
36 cache[key] = {value, std::prev(order.end())};
37 }
38
39private :
40 int capacity;
41 std::unordered_map<int, std::pair<int, std::list<int>::iterator>> cache; // 键,值,迭代器
42 std::list<int> order;
43};