不太优雅的解法,因为没用STL提供的Hashmap,所以这里手动创建一个Hashmap类来模拟哈希表,因此占用空间比较多。题目设计缓存的容量最大就是10001,因此就开一个这么大的数组用于模拟哈希表。
// account 用于记录链表中当前有多少节点(哈希表中有多少个位置是存了元素的)
int account = 0;
/* 链表节点定义
* key对应哈希表的key
* pre是指向前面节点的指针
* next是指向后面节点的指针
*/
class LinkListNode
{
public:
LinkListNode(int pkey = -1) : key(pkey), pre(nullptr), next(nullptr) {}
int key;
LinkListNode* pre;
LinkListNode* next;
};
/* 哈希表定义
* exist标记当前key是否存在,存在为0,不存在为-1
* 当前key对应的value
* pointer指向当前key对应链表中的节点
*/
class Hashmap
{
public:
Hashmap() : exist(-1), value(0), pointer(nullptr) {}
int exist;
int value;
LinkListNode* pointer;
};
/* 链表定义
* 带有头结点head和尾节点tail的双链表
* insert是头插法插入链表
* pop从尾部弹出节点
* insidePop对应是题中的get操作,从链表中间取出一个元素
*
*/
class LinkList
{
public:
LinkList() { head->next = tail; tail->pre = head;}
void insert(LinkListNode* newNode);
void pop(Hashmap hashmap[]);
void insidePop(Hashmap hashmap[], int key);
LinkListNode* head = new LinkListNode();
LinkListNode* tail = new LinkListNode();
};
// 链表头插操作
void LinkList::insert(LinkListNode* newNode)
{
newNode->next = head->next;
head->next->pre = newNode;
newNode->pre = head;
head->next = newNode;
}
// 链表尾弹出元素操作
void LinkList::pop(Hashmap hashmap[])
{
// 链表操作
LinkListNode *tmp = tail->pre;
// cout << tmp->key << "被逐出了" << endl;
tail->pre = tail->pre->pre;
tail->pre->next = tail;
--account;
// 哈希表操作
hashmap[tmp->key].exist = -1;
hashmap[tmp->key].pointer = NULL;
delete(tmp);
}
// 链表中间弹出元素操作
void LinkList::insidePop(Hashmap hashmap[], int key)
{
LinkListNode *tmp = hashmap[key].pointer;
tmp->pre->next = tmp->next;
tmp->next->pre = tmp->pre;
}
class LRUCache {
public:
LRUCache(int capacity) { this->capacity = capacity;
}
int get(int key) {
if (hashmap[key].exist != 0)
return -1;
else
list.insidePop(hashmap, key);
list.insert(hashmap[key].pointer); // 从链表中间取出节点
return hashmap[key].value; // 将节点插入链表头部
}
void put(int key, int value) {
if (hashmap[key].exist != -1) // 哈希表中对应元素存在
{
hashmap[key].value = value; // 如果这个key存在则赋新值
list.insidePop(hashmap, key); // 从链表中间取出节点
list.insert(hashmap[key].pointer); // 将节点插入链表头部
}
else {
if ( account == capacity ) // 若缓存已满
list.pop(hashmap); // 弹出尾部元素
hashmap[key].exist = 0;
hashmap[key].value = value;
LinkListNode *newNode = new LinkListNode(key);
list.insert(newNode);
hashmap[key].pointer = newNode;
++account;
}
}
private:
Hashmap hashmap[10001] ;
LinkList list;
int capacity;
};
本来是可以用单链表就解决问题的,但是STL提供的forward_list并没有暴露其指针的接口,这就导致了我们无法移动元素,而只能先删除元素再进行插入来替代移动元素,同时forward_list不支持删除当前迭代器指向的元素,只支持删除当前迭代器之后的元素,所以这里最终采用双链表来完成。
其中使用到了双链表的pop_back, emplace_front, splice(搬动元素)等函数
class LRUCache {
public:
LRUCache(int capacity) {
cap = capacity;
}
int get(int key) {
// 若哈希表中没有key则返回-1
if (cachemap.find(key) == cachemap.end())
return -1;
// 有则更新key所在元素的位置
cachelist.splice(cachelist.begin(), cachelist, cachemap[key]);
cachemap[key] = cachelist.begin();
return (*cachemap[key]).second;
}
void put(int key, int value) {
// 若缓存中有这个元素,即哈希表中有key,则更新哈希表与链表
if (cachemap.find(key) != cachemap.end())
{
(*cachemap[key]).second = value;
cachelist.splice(cachelist.begin(), cachelist, cachemap[key]);
cachemap[key] = cachelist.begin();
// update(key);
// 若缓存中没有这个元素,且插入操作会使得缓存爆了,则删除末尾的元素。然后插入
} else if (cachemap.size() == cap)
{
cachemap.erase(cachelist.back().first);
cachelist.pop_back();
cachelist.emplace_front(key, value);
cachemap[key] = cachelist.begin();
// 否则直接插入该元素
} else {
cachelist.emplace_front(key, value);
cachemap[key] = cachelist.begin();
}
}
private:
// 定义哈希表和双链表
unordered_map>::iterator> cachemap;
list> cachelist;
// 定义容量
int cap;
};
解法3:使用STL的哈希表和自己定义的链表来完成
注意用自己定义的链表,new了在用不到以后要delete来释放内存。那么LRUCache类就需要析构函数
class listNode{
public:
listNode(int k, int v, listNode *p = nullptr, listNode *n = nullptr) : key(k), val(v), pre(p), next(n) {}
int val;
int key;
listNode* pre;
listNode* next;
};
class LRUCache {
public:
LRUCache(int capacity) {
cap = capacity;
head = new listNode(-1, -1);
tail = new listNode(-1, -1);
head->next = tail;
tail->pre = head;
}
~LRUCache() {
while (head != nullptr)
{
listNode* tmp = head;
head = head->next;
delete(tmp);
}
}
int get(int key) {
if (cachemap.find(key) == cachemap.end())
return -1;
// 更新节点位置
cachemap[key]->pre->next = cachemap[key]->next;
cachemap[key]->next->pre = cachemap[key]->pre;
cachemap[key]->next = head->next;
head->next = cachemap[key];
cachemap[key]->pre = head;
cachemap[key]->next->pre = cachemap[key];
return cachemap[key]->val;
}
void put(int key, int value) {
// 若缓存中有这个元素,即哈希表中有key,则更新哈希表与链表
if (cachemap.find(key) != cachemap.end())
{
cachemap[key]->val = value;
cachemap[key]->pre->next = cachemap[key]->next;
cachemap[key]->next->pre = cachemap[key]->pre;
cachemap[key]->next = head->next;
head->next = cachemap[key];
cachemap[key]->pre = head;
cachemap[key]->next->pre = cachemap[key];
// 若缓存中没有这个元素,且插入操作会使得缓存爆了,则删除末尾的元素。然后插入
} else {
if (cachemap.size() == cap)
{
listNode* tmp = tail->pre;
cachemap.erase(tail->pre->key);
tail->pre->pre->next = tail;
tail->pre = tail->pre->pre;
delete(tmp);
}
// 插入新节点
listNode *newnode = new listNode(key, value, head, head->next);
cachemap[key] = newnode;
newnode->next->pre = newnode;
head->next = newnode;
}
}
private:
unordered_map cachemap;
listNode* head;
listNode* tail;
int cap;
};