【算法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,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...
随机推荐
- zg 作业一
作业一: 将xxjjT01增加窗体及按钮(增.修.删) 表单(form1.html) 设定按钮作业,授权 1.新增:XXJJT01+CREATE 2.修改:XXJJT01+UPDATE 3.删除:XX ...
- spring security 非页面登录
参考https://stackoverflow.com/questions/36937414/auto-login-spring-security UserDetails userDetails = ...
- python之字符串【str】
#Auther Bob#--*--conding:utf-8 --*-- #定义一个str的对象,有下面两种方法name = 'Bob abc'job = str('it')print(type(na ...
- win下php5.4安装ffmpeg-php扩展
1.ffmpeg的官网没有提供ffmpeg-php dll的扩展下载. http://ffmpeg-php.sourceforge.net/ 虽然在http://sourceforge.net/上提供 ...
- mysql自定义函数收集
代码: 查找字符串 in_string 中,存在多少个字符串 in_find_str delimiter $$ DROP FUNCTION IF EXISTS `fn_findCharCount` $ ...
- cookies,sessionStorage 和 localStorage 的区别
请描述一下 cookies,sessionStorage 和 localStorage 的区别? sessionStorage 和 localStorage 是HTML5 Web Storage AP ...
- DB2数据库常用命令数据库学习
DB2数据库常用命令数据库学习你可以用 get snapshot for locks on XXX 看是那个表锁了,再从相关的操作去查原因吧 db2pd -d 库名 -locks和db2pd -d 库 ...
- loadrunner12.5-vugen回放脚本提示:URL=“http://www.testclass.net/js/scripts.js”的常规连接当前无套接字 (16 不足) 可用,是什么意思呢?怎么理解呢?
会发生这个报错,是因为每个浏览器都有一个限制,检查哪个浏览器客户正在模拟, 通常只允许16个并发连接. 如果超过此超过接数,将显示该消息,通知您没有可用的连接. 而max connection的默认值 ...
- AJAX 请求中多出了一次 OPTIONS 请求 导致 Laravel 中间件无法对 Header 传入的 Token 无法获取
背景知识: 我们会发现,在很多post,put,delete等请求之前,会有一次options请求.本文主要是来讨论一下这是什么原因引起的. 根本原因就是,W3C规范这样要求了!在跨域请求中,分为简单 ...
- Java数据结构和算法(一)概念
Java数据结构和算法(一)概念 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 一.逻辑结构 数据之间的相互关系称为逻辑结构 ...