【算法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,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...
随机推荐
- 两个onCreate方法?你真的了解onCreate()么?
Activity的onCreate方法一直是我们编写一个activity最先重载的方法.细心的小伙伴在编写代码的时候回看到这样一幕: 咦,这里怎么会有两个onCreate提供给我们重载?选择困难症患者 ...
- poj1417(带权并查集+背包DP+路径回溯)
题目链接:http://poj.org/problem;jsessionid=8C1721AF1C7E94E125535692CDB6216C?id=1417 题意:有p1个天使,p2个恶魔,天使只说 ...
- java.lang.Error: Unresolved compilation problem: 解决方案
严重: Allocate exception for servlet WX_Interfacejava.lang.Error: Unresolved compilation problem: The ...
- client / server端用户的登录
# 客户端 import socket import hashlib import json import os import struct sk = socket.socket() # 实例化 sk ...
- tortoise svn 请求的名称有效,但是找不到请求的类型的数据
可能是博客园出bug了,现在无法放图,间断重复刷新十多次依然如故,弃之 这个问题是在安装完svn服务器(visual svn)后复制url,tortoise svn import 粘贴url后 出现的 ...
- iOS 基于MVC设计模式的基类设计
iOS 基于MVC设计模式的基类设计 https://www.jianshu.com/p/3b580ffdae00
- phpcms如何给已有的模块添加新功能?
phpcms如何给已有的模块添加新功能? 方法一:直接在模块里的控制器文件中添加功能. 不建议使用此方法,因为一旦phpcms升级,有可能会覆盖模块中的文件, 导致你添加的功能丢失. 方法二:新建一个 ...
- struts框架之总结OGNL表达式的特殊的符号
1. # 符号的用法 * 获得contextMap中的数据 > <s:property value="#request.name"/> > <s:pr ...
- jQuery中animate()对Firefox无效的解决办法
在使用 animate()做返回顶部的动画时,会出现对Firefox无效的情况,如: $('body').animate({scrollTop:'0'},500); 它对Chrome,IE,Opera ...
- socket编程介绍
Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...