【算法33】LRU算法
题目来源
LeetCode: https://leetcode.com/problems/lru-cache/
LRU简介
LRU (Least Recently Used,最近最少使用)算法是操作系统中一种经典的页面置换算法,当发生缺页中断时,需要将内存的一个或几个页面换出,LRU指出应该将内存最近最少使用的那些页面进行换出,依据的是程序的局部性原理,最近经常使用的页面在不久的将来也很有可能被使用,反之最近很少使用的页面未来也不太可能再使用。
LRU 数据结构
LRU采用双向链表+hash表的数据结构实现,双向链表作为队列存储当前缓存节点,其中从表头到表尾的元素按照最近使用的时间进行排列,放在表头的是最近刚刚被使用过的元素,表尾的最近最少使用的元素;如果仅仅采用双向链表,那么查询某个元素需要 O(n) 的时间,为了加快双向链表中元素的查询速度,采用hash表讲key进行映射,可以在O(1)的时间内找到需要节点。
LRU主要实现以下两个接口:
int Get(int key);
void Put(int key, int value);
其中 Get 用来读取队列中的元素,同时需要将该元素移动到表头;Put 用来向队列中插入元素。
LRU 具体实现
从实现的角度来看,每次 Get 时, 需要判断该 key 是否在队列中,如果不在,返回-1;如果在,需要重新移动该元素到表头位置(具体实现,可以先删除,在插入到表头)。 每次 Put 时,首先需要判断key是否在队列中,如果在,那么更新其 value值,然后移动该元素到表头即可;如果不在,需要进一步判断,队列是否已满,如果已满;那么需要首先删除队尾元素,并对 size - 1, 删除哈希表中对应元素的 key;然后在插入新的元素到队头。
具体C++代码如下:
/**
* LRU Cache Implementation using DoubleLinkList & hashtable
* Copyright 2015 python27
* 2015/06/26
*/
#include <iostream>
#include <string>
#include <map>
#include <list>
#include <deque>
#include <cassert>
#include <cstdio>
#include <cstdlib>
using namespace std; struct CacheNode
{
int key;
int value;
CacheNode* prev;
CacheNode* next; CacheNode(int k, int v) : key(k), value(v), prev(NULL), next(NULL)
{} CacheNode():key(), value(), prev(NULL), next(NULL)
{}
}; class LRUCache
{
public:
LRUCache(int capacity); int Get(int key);
void Put(int key, int value); public:
void PrintList() const;
private:
void InsertNodeFront(CacheNode* p);
void DeleteNode(CacheNode* p); private:
map<int, CacheNode*> m_hashtable; // hash table
CacheNode* m_head; // double link list head
CacheNode* m_tail; // double link list tail
int m_capacity; // capacity of link list
int m_size; // current size of link list
}; void LRUCache::PrintList() const
{
CacheNode* p = m_head;
for (p = m_head; p != NULL; p = p->next)
{
printf("(%d, %d)->", p->key, p->value);
}
printf("\n");
printf("size = %d\n", m_size);
printf("capacity = %d\n", m_capacity);
} LRUCache::LRUCache(int capacity)
{
m_capacity = capacity;
m_size = ;
m_head = NULL;
m_tail = NULL;
} // insert node into head pointed by p
void LRUCache::InsertNodeFront(CacheNode* p)
{
if (p == NULL) return; if (m_head == NULL)
{
m_head = p;
m_tail = p;
}
else
{
p->next = m_head;
m_head->prev = p;
m_head = p;
}
} // delete node in double linklist pointed by p
void LRUCache::DeleteNode(CacheNode* p)
{
if (p == NULL) return; assert(m_head != NULL && m_tail != NULL); if (m_size == )
{
if (p == m_head && p == m_tail)
{
delete p;
m_head = NULL;
m_tail = NULL;
}
else
{
fprintf(stderr, "Delete Wrong! No such Node");
return;
}
}
else if (p == m_head)
{
m_head = m_head->next;
m_head->prev = NULL;
delete p;
}
else if (p == m_tail)
{
m_tail = m_tail->prev;
m_tail->next = NULL;
delete p;
}
else
{
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
} } int LRUCache::Get(int key)
{
// if key not in return -1
if (m_hashtable.find(key) == m_hashtable.end())
{
return -;
} CacheNode* p = m_hashtable[key];
int k = p->key;
int v = p->value; // delete this node
DeleteNode(p); // insert this node to the head
p = new CacheNode(k, v);
InsertNodeFront(p);
// update hash table
m_hashtable[k] = p;
return p->value;
} void LRUCache::Put(int key, int value)
{
// if key alread in, update
if (m_hashtable.find(key) != m_hashtable.end())
{
CacheNode* p = m_hashtable[key]; // delete node
DeleteNode(p);
// insert node
p = new CacheNode(key, value);
InsertNodeFront(p);
// update hash table
m_hashtable[key] = p;
return;
}
// if list is full, delete the tail node
else if (m_size >= m_capacity)
{
// delete the tail node
CacheNode* p = m_tail;
m_hashtable.erase(p->key);
DeleteNode(p);
m_size--;
} // create node and insert into head
assert(m_size < m_capacity);
CacheNode* p = new CacheNode(key, value);
InsertNodeFront(p);
m_hashtable[key] = p;
m_size++;
} int main()
{
LRUCache lru();
lru.Put(, );
lru.PrintList();
lru.Put(, );
lru.PrintList();
lru.Put(, );
lru.PrintList();
lru.Put(, );
lru.PrintList();
int value = lru.Get();
printf("Get(3) = %d\n", value);
lru.PrintList();
value = lru.Get();
printf("Get(2) = %d\n", value);
lru.PrintList();
value = lru.Get();
printf("Get(4) = %d\n", value);
lru.PrintList();
value = lru.Get();
printf("Get(1) = %d\n", value);
lru.PrintList(); return ;
}
【算法33】LRU算法的更多相关文章
- 【算法】—— LRU算法
LRU原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”. 实现1 最常见的 ...
- 【算法】LRU算法
缓存一般存放的都是热点数据,而热点数据又是利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰出来的. 出于对这个算法的好奇就查了下资料. LRU算法四种实现方式介绍 缓存淘汰算法 利用Linked ...
- Redis内存管理中的LRU算法
在讨论Redis内存管理中的LRU算法之前,先简单说一下LRU算法: LRU算法:即Least Recently Used,表示最近最少使用页面置换算法.是为虚拟页式存储管理服务的,是根据页面调入内存 ...
- LRU 算法
LRU算法 很多Cache都支持LRU(Least Recently Used)算法,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定 ...
- Redis内存回收:LRU算法
Redis技术交流群481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read Redis中采用两种算法进行内存回收,引用计数算法以及LRU ...
- LRU算法原理解析
LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...
- 二叉树遍历问题、时间空间复杂度、淘汰策略算法、lru数据结构、动态规划贪心算法
二叉树的前序遍历.中序遍历.后序遍历 前序遍历 遍历顺序规则为[根左右] ABCDEFGHK 中序遍历 遍历顺序规则为[左根右] BDCAEHGKF 后序遍历 遍历顺序规则为[左右根] DCBHKGF ...
- LRU算法详解
一.什么是 LRU 算法 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置.但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留 ...
- Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?
在<Redis 数据缓存满了怎么办?>我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据. 淘汰策略如下所示: 设置过期时间的 key volatile-ttl.vo ...
- 缓存淘汰算法--LRU算法
1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...
随机推荐
- 大型运输行业实战_day05_1_登录+注销+表单重复提交+登录拦截器
1.登录 登录实现如下步骤: 1.在首页中添加登录按钮 html代码如下: <%@ page contentType="text/html;charset=UTF-8" la ...
- Java8 Stream语法详解 2
1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...
- np.random.seed()
124.np.random.seed()的作用 陈容喜 关注 2018.01.11 21:36 字数 3 阅读 4460评论 0喜欢 6 今天看到一段代码时遇到了np.random.seed(),搞不 ...
- 16-图片上传那些事 org.springframework.web.multipart.MultipartException: The current request is not a multipart request
https://blog.csdn.net/u010974598/article/details/46458039 我曾尝试了: @RequestMapping(value="/user/r ...
- NetworkStateReceiver的简单应用
一.网络状态接收者是一个广播接收者当网络状态发生改变时会收到网络状态改变的广播 本例当网络状态改变时会进行相应处理 例如当断网时会发出通知点击通知后 会打开网络设置界面 代码如下: package c ...
- spring中配置监听队列的MQ
一.spring中配置监听队列的MQ相关信息注:${}是读取propertites文件的常量,这里忽略.绿色部分配置在接收和发送端都要配置. <bean id="axx" ...
- struts框架中OGNL表达式的使用之jsp页面获取action中的属性值
在jsp页面中获取action中的值: 1.写一个action类OgnlAction类: 需要注意的地方: 如果在aciton中直接使用ognl表达式,将值存储的值栈中,是不能通过跳转将值传到jsp页 ...
- Codeforces 710C. Magic Odd Square n阶幻方
C. Magic Odd Square time limit per test:1 second memory limit per test:256 megabytes input:standard ...
- 680C. Bear and Prime 100 数学
C. Bear and Prime 100 time limit per test:1 second memory limit per test:256 megabytes input:standar ...
- Codeforces 611C. New Year and Domino 动态规划
C. New Year and Domino time limit per test 3 seconds memory limit per test 256 megabytes input stand ...