题目如下,没有想到大二学的操作系统还能用上,先解释一下什么叫LRU吧。LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的。LRU算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面很可能在后面的几条指令中频繁使用。反过来说,已经很久没有使用的页面很可能在未来较长的一段时间内不会被用到。这个,就是著名的局部性原理——比内存速度还要快的cache,也是基于同样的原理运行的。因此,我们只需要在每次调换时,找到最近最久未使用的那个页面调出内存。这就是LRU算法的全部内容。
页面调度自行百度吧,很难三言两语说清楚,大概意思就是Cache很小,内存比较大,但是Cache很快,所以计算机会把程序分页放到Cache上执行,但是一个程序可能很大,Cache装不下,所以需要页面调度,就是说计算机现在要执行什么指令或者需要什么数据了,但是所需要的这个页面又刚好不在Cache中,那么就要从内存中找了,内存还没有就从硬盘中找了,当然这都是后话,原理都是一样的,在内存中找到了怎么办呢?Cache中已经没有空间了,那么就只能把Cache中的页面换出去,把需要的页面换进来,但是顺序调度时很浪费时间的,所以需要一个合理的调度算法来调度,LRU就是一个比较合理的
LRU即最近最少使用,那么怎么知道一个页面时最近最少使用呢?一个办法就是排个序,那么问题来了,一个最近,一个最少,怎么处理?书上的方法是当一个页被执行时,把这个页放到最前面,当需要置换时把最后一个置换出去,这么一来最后一个可以保证时最近这段时间最少使用过的,因为最近使用过的都在前秒你
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.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // returns 1 cache.put(3, 3); // evicts key 2 cache.get(2); // returns -1 (not found) cache.put(4, 4); // evicts key 1 cache.get(1); // returns -1 (not found) cache.get(3); // returns 3 cache.get(4); // returns 4题目要求get和put方法都要时O(1)的时间复杂度,也就是说查找,插入删除都得是O(1)的时间复杂度,那么问题来了,我们目前学过的数据结构只有链表插入删除是O(1),其他都达不到插入删除为O(1),但是链表做不到查找也是O(1)啊,所以只能大胆猜测,通过key作为数组的index来存链表的地址,数组的查找可以为O(1),那么问题就解决了,我也是想了很久才想到的啊。剩下的就打代码了,思路都给出来了。
class List{//链表数据结构
public:
int val;
int key;
List* next;
List* pre;
};
class LRUCache {
public:
List* head;//链表头
List* tail;//链表尾
int cap;//总容量
int num;//当前容量
List* a[10000];//链表数组
LRUCache(int capacity) {//初始化
cap=capacity;
num=0;
for(int i=0;i<10000;i++)
{
a[i]=NULL;
}
}
int get(int key) {
int ans;//返回结果
if(a[key]==NULL)
return -1;
else
{
ans=a[key]->val;
List *temp=a[key];
if(temp!=head)//如果是头的话就不用继续操作了,因为它已经在最前面了
{
if(temp!=tail)//这个节点在中间,先删除
{
temp->pre->next=temp->next;
temp->next->pre=temp->pre;
}
else//在尾部,删除
{
temp->pre->next=NULL;
tail=temp->pre;
}
temp->pre=NULL;//插入到头部
temp->next=head;
head->pre=temp;
head=temp;//重新赋值头部,因为这个时候这个节点在最前面
}
}
return ans;
}
void put(int k, int value) {
List* temp=new List;
temp->key=k;
temp->val=value;
temp->pre=NULL;
if(a[k]!=NULL)//一个坑,题目会反复插入同一个key但value不同的数据
{
if(a[k]->val!=value)//如果value同就不操作
{
a[k]->val=value;
int s=get(k);//使用get方法来把它放到最前面
}
return;
}
if(num==0)//空链表,头尾特殊处理
{
temp->next=NULL;
head=temp;
tail=head;
num++;
}
else//不为空,正常操作,放到最前面
{
temp->next=head;
head->pre=temp;
head=temp;
num++;
}
if(num>cap)//超过容量,需要删除最后一个
{
List* d=tail;
d->pre->next=NULL;
tail=d->pre;//重新赋值尾
a[d->key]=NULL;
delete d;
num--;
}
a[k]=temp;
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
大概就是这样吧,时间消耗还行吧,超过了94%的人,但是做了挺久的,因为很少用链表,出了不少问题。也算是温习了一下吧,另外操作系统也要好好学啊。