LRU缓存机制C++泛型实现
题目如LeetCode 146题
运用你所掌握的数据结构,设计和实现一个LRU(最近最少使用)缓存机制。它应该支持以下操作:获取数据get
和写入数据put
。
获取数据get(key)
- 如果关键字(key)存在于缓存中,则获取关键字的值(总是正数),否则返回-1.
写入数据put(key, value)
- 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组 [关键字/值] 。当缓存容量达到上限时,他应该在写入新数据之前删除最久未使用的数据值,从而为新数据值留出空间。
实现
题目中键值对都为int型数据,想着能否利用模板实现一个泛型LRU。本人水平有限,代码如有错误欢迎指出。
LRU实现利用哈希表和双向链表结构实现。首先是链表节点定义:
#include <unordered_map>
#include <iostream>
using namespace std;
template <class Key, class Value>
struct DoubleListNode
{
typedef DoubleListNode<Key, Value> SelfType;
typedef Key KeyType;
typedef Value ValueType;
KeyType key;
ValueType value;
SelfType *prev;
SelfType *next;
DoubleListNode(const KeyType &iKey, const ValueType &iVal)
: key(iKey), value(iVal), prev(nullptr), next(nullptr) {}
};
然后是链表的实现:
template <class Key, class Value>
class DoubleLinkedList
{
public:
class __Iterator;
typedef DoubleListNode<Key, Value> NodeType;
typedef Key KeyType;
typedef Value ValueType;
typedef __Iterator Iterator;
// 迭代器类型
class __Iterator
{
private:
NodeType *m_node;
public:
__Iterator() : m_node(nullptr) {}
__Iterator(NodeType *node) : m_node(node) {}
__Iterator(const __Iterator &rhs) : m_node(rhs.m_node){}
// 等于运算符
bool operator== (const __Iterator &rhs) const
{
return m_node == rhs.m_node;
}
// 不等于运算符
bool operator!= (const __Iterator &rhs) const
{
return m_node != rhs.m_node;
}
// 解引用运算符
Value& operator* () const
{
return m_node->value;
}
// 获取所指节点的key
Key key() const
{
if (m_node == nullptr)
return Key();
return m_node->key;
}
// 获取所指节点的value
Value value() const
{
if (m_node == nullptr)
return value();
return m_node->value;
}
// 为了能够让链表能够访问私有数据成员
friend class DoubleLinkedList<Key, Value>;
};
private:
size_t m_len;
NodeType *m_head;
NodeType *m_tail;
public:
DoubleLinkedList()
: m_len(0), m_head(nullptr), m_tail(nullptr) {}
~DoubleLinkedList()
{
for (size_t i = 0; i < m_len; i++)
removeBack();
}
Iterator begin() const
{
return Iterator(m_head);
}
Iterator end() const
{
return Iterator(nullptr);
}
bool empty() const
{
return m_head == nullptr;
}
size_t length() const
{
return m_len;
}
// 在头部插入
Iterator insertFront(const KeyType &key, const ValueType &value)
{
NodeType *pNode = new NodeType(key, value);
insertFront(pNode);
return begin();
}
// 将迭代器所指元素移到头部
void moveToFront(Iterator iter)
{
NodeType *pNode = iter.m_node;
remove(pNode);
insertFront(pNode);
}
// 尾元素
Iterator back() const
{
return Iterator(m_tail);
}
// 删除尾元素
void removeBack()
{
// 链表为空则什么也不做
if (m_tail == nullptr)
return;
// 如果链表只有一个元素
if (m_tail == m_head)
{
delete m_tail;
m_head = m_tail = nullptr;
}
// 链表有多个元素
else
{
NodeType *pNode = m_tail;
m_tail = m_tail->prev;
m_tail->next = nullptr;
delete pNode;
}
--m_len;
}
private:
// 在头部插入节点
void insertFront(NodeType *pNode)
{
if (m_head == nullptr)
m_head = m_tail = pNode;
else
{
pNode->next = m_head;
m_head->prev = pNode;
m_head = pNode;
}
++m_len;
}
// 将指针所指元素删除但是并不释放其空间
void remove(NodeType *pNode)
{
if (pNode == nullptr)
return;
if (pNode->prev != nullptr)
pNode->prev->next = pNode->next;
if (pNode->next != nullptr)
pNode->next->prev = pNode->prev;
if (pNode->prev == nullptr)
m_head = pNode->next;
if (pNode->next == nullptr)
m_tail = pNode->prev;
pNode->prev = nullptr;
pNode->next = nullptr;
--m_len;
}
};
最后是LRU的实现:
template <class Key, class Value>
class LRUCache
{
public:
typedef DoubleListNode<Key, Value> NodeType;
typedef DoubleLinkedList<Key, Value> ListType;
private:
size_t m_capacity;
unordered_map<Key, typename ListType::Iterator> m_cache;
ListType m_list;
public:
LRUCache(size_t capacity)
: m_capacity(capacity) {}
~LRUCache() {}
Value get(const Key &key, bool &isOk)
{
// 如果存在该key
if (m_cache.count(key))
{
isOk = true;
auto iter = m_cache[key];
m_list.moveToFront(iter);
return *iter;
}
// 如果不存在该key
isOk = false;
return Value();
}
void put(const Key &key, const Value &value)
{
// 如果存在该key
if (m_cache.count(key))
{
auto iter = m_cache[key];
*iter = value;
m_list.moveToFront(iter);
return;
}
// 如果不存在该key
// 如果缓存已满
if (m_list.length() == m_capacity)
{
auto iter = m_list.back();
m_cache.erase(iter.key());
m_list.removeBack();
}
auto iter = m_list.insertFront(key, value);
m_cache[key] = iter;
}
};
测试代码:
int main()
{
LRUCache<int, int> cache(2);
bool isOk;
int result;
cache.put(1, 1);// [(1, 1)]
cache.put(2, 2);// [(2, 2), (1, 1)]
result = cache.get(1, isOk);// [(1, 1), (2, 2)]
cout << "isOK: " << isOk << " value: " << result << endl;
cache.put(3, 3);// [(3, 3), (1, 1)]
result = cache.get(2, isOk);
cout << "isOK: " << isOk << " value: " << result << endl;
cache.put(4, 4);// [(4, 4), (3, 3)]
result = cache.get(1, isOk);
cout << "isOK: " << isOk << " value: " << result << endl;
result = cache.get(3, isOk);// [(3, 3), (4, 4)]
cout << "isOK: " << isOk << " value: " << result << endl;
result = cache.get(4, isOk);// [(4, 4), (3, 3)]
cout << "isOK: " << isOk << " value: " << result << endl;
return 0;
}
测试结果为: