一道LeetCode OJ上的题目,要求设计一个LRU(Least Recently Used)算法,题目描述如下:

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(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.

花了一个上午的时间来做这道题,终于Accepted了。感觉这道题出的非常好,做完这道题,对LRU算法的理解就不只局限在理论上了。LRU算法是一种cache置换算法,当cache容量不够时,将最久未使用的条目置换出去,换入新的当前需要使用的条目。

既然是设计题,那么首先就要确定数据结构。如何快速定位某条记录是否位于cache中,可以使用map,map的内部机制是一颗红黑树,时间复杂度为O(logn),但是我使用了另一个结构——unordered_map,这个关联容器是C++11中新加入的,利用hash函数进行定位,时间复杂度为O(1)。将unordered_map初始化为unordered_map<key, value>,就能快速读写某个key对应的记录了。下面考虑如何实现LRU的相关算法。既然每次要淘汰最久未使用的记录,那么可以使用一个优先队列,队列开头存放最久未使用的记录,队列结尾存放最近刚刚被使用(读或写)过的记录。当需要换出记录时,选择优先队列开头的元素即可;当读、写完某条记录后,需要将该记录放到优先队列末尾,这里分两种情况:

  1. Cache中存在这条记录,则将记录移动到队列尾部。
  2. Cache中不存在这条记录,则将记录加入到队列尾部。

那么问题来了,用C++中的什么结构充当这个优先队列呢?STL中的queue和priority_queue肯定不行,因为这两个数据结构只有两端开放,无法遍历内部元素,这对于上面的情况1是不适用的。刚开始我想到了使用vector,使用完某个记录后就在vector中线性查找该记录,然后将它移动到vector末尾。这种操作的效率是O(n),提交后系统出现了“Time Limit Exceeded”的错误,看来光有线性时间复杂度是不行的。后来想到了list,在list上移动一个节点只需要常数时间,我将list中的每一个节点关联到unordered_map的value成员上,也就是unordered_map<key, node*>。这样一来,查找某条记录、修改优先队列这两个操作都只需要常数时间。说起来有一点拗口,代码表达的意思更加直白,整个代码如下所示:

/*
* https://oj.leetcode.com/problems/lru-cache/
*/ #include <iostream>
#include <vector>
#include <unordered_map> using namespace std; class Node {
public:
int key;
int value;
Node *prev;
Node *next; Node(int k, int v) : key(k), value(v), prev(NULL), next(NULL)
{}
}; class LinkList {
public:
int capacity;
int size;
Node *first;
Node *last; LinkList(int c) : capacity(c), size()
{
first = new Node(, );
last = new Node(, );
first->next = last;
last->prev = first;
}
}; class LRUCache{
public:
LRUCache(int capacity) : PriorityLink(capacity)
{} int get(int key)
{
if (mp.find(key) != mp.end())
{
Node *pNode = mp[key];
if (pNode->next != PriorityLink.last)
{
unlink(pNode);
link(pNode);
}
return pNode->value;
}
else
return -;
} void set(int key, int value)
{
if (mp.find(key) != mp.end())
{
Node *pNode = mp[key];
pNode->value = value;
if (pNode->next != PriorityLink.last)
{
unlink(pNode);
link(pNode);
}
}
else
{
if (PriorityLink.size == PriorityLink.capacity)
{
// full
Node *remove = PriorityLink.first->next;
unlink(remove);
mp.erase(remove->key);
delete remove;
}
else
PriorityLink.size++; Node *add = new Node(key, value);
link(add);
mp[key] = add;
}
} void link(Node *pNode)
{
// put node to back of PriorityLink
pNode->prev = PriorityLink.last->prev;
pNode->next = PriorityLink.last;
PriorityLink.last->prev = pNode;
pNode->prev->next = pNode;
} void unlink(Node *pNode)
{
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
pNode->prev = NULL;
pNode->next = NULL;
} LinkList PriorityLink;
unordered_map<int, Node*> mp;
}; int main()
{
LRUCache mem();
mem.set(, );
mem.set(, );
cout << mem.get();
cout << mem.get();
mem.set(, );
//mem.set(4, 1);
cout << mem.get();
cout << mem.get();
cout << mem.get();
system("pause");
return ;
}

LRU算法的设计的更多相关文章

  1. LRU算法的Java实现

    LRU全称是Least Recently Used,即最近最久未使用的意思. LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定的空间已 ...

  2. LRU 算法

    LRU算法 很多Cache都支持LRU(Least Recently Used)算法,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定 ...

  3. LRU算法原理解析

    LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...

  4. 基于LinkedhashMap实现的LRU算法

    LRU全称是Least Recently Used,即最近最久未使用的意思.LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定的空间已存 ...

  5. Guava---缓存之LRU算法

    随笔 - 169  文章 - 0  评论 - 292 GuavaCache学习笔记一:自定义LRU算法的缓存实现   前言 今天在看GuavaCache缓存相关的源码,这里想到先自己手动实现一个LRU ...

  6. LeetCode之LRU Cache 最近最少使用算法 缓存设计

    设计并实现最近最久未使用(Least Recently Used)缓存. 题目描述: Design and implement a data structure for Least Recently ...

  7. redis的LRU算法(二)

    前文再续,书接上一回.上次讲到redis的LRU算法,文章实在精妙,最近可能有机会用到其中的技巧,顺便将下半部翻译出来,实现的时候参考下. 搏击俱乐部的第一法则:用裸眼观测你的算法 Redis2.8的 ...

  8. LRU算法与LRUCache

    关于LRU LRU(Least recently used,最近最少使用)算法是操作系统中一种经典的页面置换算法,当发生缺页中断时,需要将内存的一个或几个页面置换出,LRU指出应该将内存最近最少使用的 ...

  9. 如何实现LRU算法?

    1.什么是LRU算法? LRU是一种缓存淘汰机制策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新的内容腾位置.但是要删除哪些内容呢?我们肯定希望删掉那些没有用的缓存,而把有用的数据继续 ...

随机推荐

  1. Struts 2的iterator标签来遍历一个含有双层List的嵌套

    今天碰到一个很有意思的问题,就是需要用Struts 2的iterator标签来遍历一个含有双层List的嵌套. 首先我们从最基础的说起,用iterator标签遍历一个List. 如果Action中有一 ...

  2. 安装虚拟机VMWare时出现1021错误的解决办法

    今天安装虚拟机(VMWare Workstation9.0),中途老是出现错误:Failed to create the requested registry key key installer er ...

  3. 利用border和伪类画出三角形 ps:好久没写博客了。。。

    有一个半月没有写博客了,这段时间,小哥我经历了自入行前端最为黑暗的时期,迷茫,空虚,不想写代码,不想做研究了.连打游戏都没有兴趣,如同行尸走肉一般.还好,毕业论文的初稿完成后,整个时间段最恶心最难熬的 ...

  4. 标准C编程-笔记全集

    C语言的基本概念 编写一个简单的C程序,后缀名保存为c(本次文件名为a.c) gcc:对c程序进行编译和连接:gcc a.c ./a.out:运行程序,输出程序的结果:其中a是c程序的文件名 说明:其 ...

  5. 【HTML5】DOMContentLoaded事件

    这个事件是从HTML中的onLoad的延伸而来的,当一个页面完成加载时,初始化脚本的方法是使用load事件,但这个类函数的缺点是仅在所有资源都完全加载后才被触发,这有时会导致比较严重的延迟,开发人员随 ...

  6. jQuery判断滚动条滚到页面底部脚本

    原文地址 http://www.111cn.net/wy/jquery/61741.htm

  7. 解决cookie无法删除的问题

    今天遇到一个cookie无法删除的问题,退出操作时cookie无法删除,必须在首页先进行退出操作,后来发现一个网友的博客,介绍了无法删除Cookie的原因,原来是我关于cookie的基础知识没搞清楚. ...

  8. BadUSB的防范研究

    近期爆出的badUSB漏洞,通过将病毒植入固件,能够伪装成键盘等设备,直接控制电脑,业界还没有非常好的修复方法. 从安全产品的角度.对于这个问题的防范,有下面几点可能不成熟的想法 1.病毒伪装成键盘. ...

  9. (十一年)unity4.6得知Ugui中国文献-------参考-UGUI Visual Components

     大家好,我是太阳广东. 转载请注明出处:http://write.blog.csdn.net/postedit/38922399 更全的内容请看我的游戏蛮牛地址:http://www.unity ...

  10. PS学习

    PS快捷键大全(转自UI中国PS教程) 摆脱鼠标流就靠这张图了!!!! 查看图像 使用导航器查看图像 选择窗口-->导航器 菜单命令 使用缩放工具查看图像 ctrl++ 以画布大小放大图像 ct ...