之前介绍过hash table, 也介绍过hash map, 当然, 双向链表也早就说过, 现在来看看如何用这些东西来实现一个LruCache, 直接上代码:
#include <iostream>
#include <vector>
#include <ext/hash_map> // for hashmap
using namespace std;
using namespace __gnu_cxx; // for hashmap
template <class K, class V>
struct Node
{
K key;
V data;
Node *pPrev;
Node *pNext;
};
template <class K, class V>
class LRUCache
{
private:
hash_map<K, Node<K,V>* > m_hashmap; // O(1)查找,记录key是否存在
Node<K,V> *m_pHead;
Node<K,V> *m_pTail;
Node<K,V> *m_pEntry;
vector<Node<K,V>* > m_vecPEntry; // 管理可用结点
public:
LRUCache(size_t size)
{
m_pEntry = new Node<K,V>[size];
for(size_t i = 0; i < size; i++)
{
m_vecPEntry.push_back(m_pEntry + i); // 初始化可用结点
}
// 双向链表初始化, 引入头结点和尾结点是为了操作统一化
m_pHead = new Node<K,V>;
m_pTail = new Node<K,V>;
m_pHead->pPrev = NULL;
m_pHead->pNext = m_pTail;
m_pTail->pPrev = m_pHead;
m_pTail->pNext = NULL;
}
~LRUCache()
{
if(NULL != m_pHead)
{
delete m_pHead;
m_pHead = NULL;
}
if(NULL != m_pTail)
{
delete m_pTail;
m_pTail = NULL;
}
if(NULL != m_pEntry)
{
delete [] m_pEntry; // []不可漏
m_pEntry = NULL;
}
}
void Put(K key, V data)
{
Node<K,V> *pNode = m_hashmap[key];
if(NULL != pNode) // 该key之前已经存在,则更新值,并调整到双向链表的表头
{
detach(pNode);
pNode->data = data;
attach(pNode);
return;
}
if(m_vecPEntry.empty()) // 无可用结点,则双向链表最后的结点需要滚蛋
{
pNode = m_pTail->pPrev;
detach(pNode);
m_hashmap.erase(pNode->key);
}
else // 有可用结点,则取可用结点
{
pNode = m_vecPEntry.back();
m_vecPEntry.pop_back();
}
// 塞到双向链表的表头
pNode->key = key;
pNode->data = data;
attach(pNode);
m_hashmap[key] = pNode;
}
V Get(K key)
{
Node<K,V> *pNode = m_hashmap[key];
if(NULL != pNode) // key已经存在,该次访问后,需放在链表的头部
{
detach(pNode);
attach(pNode);
return pNode->data;
}
else // key不存在
{
return V();
}
}
private:
void detach(Node<K,V>* pNode) // 从双向链表中删除当前结点(不要delete了,后面还会用)
{
pNode->pPrev->pNext = pNode->pNext;
pNode->pNext->pPrev = pNode->pPrev;
}
void attach(Node<K,V>* pNode) // 将当前结点插入双向链表头部
{
pNode->pPrev = m_pHead;
pNode->pNext = m_pHead->pNext;
m_pHead->pNext = pNode;
pNode->pNext->pPrev = pNode;
}
};
int main()
{
LRUCache<int, string> lru_cache(1000);
lru_cache.Put(1, "hello");
cout << lru_cache.Get(1) << endl;
if(lru_cache.Get(2).empty())
{
cout << "no key" << endl;
}
lru_cache.Put(2, "world");
cout << lru_cache.Get(2) << endl;
return 0;
}
结果:
hello
no key
world
要好好体会一下。
不多说。