LeetCode 30 days Challenge - Day 24
本系列将对LeetCode新推出的30天算法挑战进行总结记录,旨在记录学习成果、方便未来查阅,同时望为广大网友提供帮助。
LRU Cache
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and put
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.put(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
1 | LRUCache cache = new LRUCache( 2 /* capacity */ ); |
Solution
题目要求分析:要求设计一个缓存机制,并实现访问get操作、put插入操作。
get操作
:当元素不存在,返回-1
。put操作
:当占满空间后再次插入内容时,将最近最少使用的元素删去,替换之。
解法:
本题的朴素解法较简单,本文介绍一种O(1)访存的设计:双向链表(带头尾指针)+ 哈希结构(键值->链表结点)
先分析,该设计如何实现O(1)的访存要求:
get操作:按照键值访问哈希表是O(1)的,找到哈希表对应的元素后,访问结点的val元素即可。
put操作:
检测是否已存在该键值(unordered_map的find函数实现如下,是O(1)的)。
1
2
3
4
5
6
7
8
9_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, _Traits>::
find(const key_type& __k) const
-> const_iterator
{
__hash_code __code = this->_M_hash_code(__k);
std::size_t __n = _M_bucket_index(__k, __code);
__node_type* __p = _M_find_node(__n, __k, __code);
return __p ? const_iterator(__p) : end();检测缓存是否已满是O(1)的。
在哈希表中删除(erase)是O(1)的。
在链表尾部删除结点、头部插入结点也是O(1)的。
综上,能保证是O(1)的访存。
接下来介绍实现中需要注意的几点:
- 删除LRU数据时依据是:通过双向链表尾结点的前一个结点,确定哈希表应该删除哪一表项。因此,链表结点除了记录数据之外,还要记录键值。
m.erase(m.find(rear->pre->key)); // 哈希表的删除语句
- put操作需要进行的判断有三种情况,勿遗漏:
- 缓存已满,插入的键值不存在:删除LRU,在头部插入新键值对(结点)。
- 插入的键值已存在,此时不需考虑缓存是否已满:查找键值对应数据,更新为新值,并移动到头部。
- 插入的键值不存在,缓存未满的情况:直接在同步插入新键值对(结点)。
1 | class LRUCache { |
传送门:LRU Cache
Karl