最近遇到一个用LRU算法设计一个缓存的题目,看了一些文章,自己再总结一下。LRU全称是Least Recently Used,意思是当一个数据在最近一段时间没有被访问的话,那么我们预测它在未来被访问的可能性也很小。所以,当缓存已经满了的时候,就应该把最近没有被访问的数据从缓存中踢出去。
思路:
1. 我最开始用的是最笨的方法,就是对于页面请求数组中的每一个数字,如果在缓存数组中找到的话,就将其前面的数字依次向后移动,然后把当前数字放在最前面;如果没找到,就将所有数字后移一位,然后把前面的数字放在最前面。这种方法在遍历、移动时的复杂度都为O(n),在缓存区查找的时候为O(m),m是缓存区大小,所以很耗时。
2. 那么怎么减少移动、遍历和查找的次数呢?采用的是双向链表+hashmap的方法,因为双向链表在插入和删除的时候,无需查找前向节点,而哈希表基本可以保证在O(1)时间内查找节点。
具体实现:
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
struct Node
{
int key;
int val;
Node* pre;
Node* next;
};
class LRUCache
{
private:
int count;
int size;
map<int, Node*> mp;
Node* head;
Node* Tail;
public:
LRUCache(int max_size_cache)
{
count = 0;
size = max_size_cache;
head = NULL;
Tail = NULL;
}
void pushFront(Node *cur)//先删除节点,再将节点移到头部,针对的是缓存中存在该节点的情况
{
if (count == 1)
return;
if (cur == head)
return;
if (cur == Tail)
{
Tail = cur->pre;
}
cur->pre->next = cur->next;
if (cur->next != NULL)
cur->next->pre = cur->pre;
cur->next = head;
cur->pre = NULL;
head->pre = cur;
head = cur;
mp[cur->key] = head;
}
int getkey(int key)
{
map<int, Node*>::iterator it = mp.find(key);
if (it==mp.end())
{
return -1;
}
else
{
Node *p = it->second;
pushFront(p);
}
return head->val;
}
void setkey(int key, int val)
{
if (head==NULL)//链表为空,直接放在头结点
{
head = new Node;
head->key = key;
head->val = val;
head->pre = NULL;
head->next = NULL;
mp[key] = head;
Tail = head;
count++;
}
else
{
map<int, Node*>::iterator it = mp.find(key);
if (it==mp.end())//没有
{
if (count==size)//cache满了
{
if (head==Tail&&head!=NULL)//只有一个节点
{
mp.erase(head->key);
head->key = key;
head->val = val;
mp[key] = head;
}
else
{
Node *p = Tail;
Tail->pre->next = Tail->next;
Tail = Tail->pre;
mp.erase(p->key);
p->key = key;
p->val = val;
p->next = head;
p->pre = head->pre;
head->pre = p;
head = p;
mp[key] = head;
}
}
else//cache没满
{
Node *p = new Node;
p->key = key;
p->val = val;
p->next = head;
p->pre = NULL;
head->pre = p;
head = p;
mp[key] = head;
count++;
}
}
else//缓存中有
{
Node *p = it->second;
p->val = val;
pushFront(p);
}
}
}
void printCache()
{
Node *p = head;
while (p != NULL)
{
cout << p->key << " ";
p = p->next;
}
cout << endl;
}
};
int main(void)
{
LRUCache cache(3);
cache.setkey(1, 1);
cache.setkey(2, 2);
cache.setkey(3, 3);
cache.printCache();
cache.setkey(4, 4);
cache.printCache();
cout << cache.getkey(4) << endl;
cache.printCache();
cout << cache.getkey(3) << endl;
cache.printCache();
cout << cache.getkey(2) << endl;
cache.printCache();
cout << cache.getkey(1) << endl;
cache.printCache();
cache.setkey(5, 5);
cache.printCache();
cout << cache.getkey(1) << endl;
cout << cache.getkey(2) << endl;
cout << cache.getkey(3) << endl;
cout << cache.getkey(4) << endl;
cout << cache.getkey(5) << endl;
return 0;
}