题目来源

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算法的更多相关文章

  1. 【算法】—— LRU算法

    LRU原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”. 实现1 最常见的 ...

  2. 【算法】LRU算法

    缓存一般存放的都是热点数据,而热点数据又是利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰出来的. 出于对这个算法的好奇就查了下资料. LRU算法四种实现方式介绍 缓存淘汰算法 利用Linked ...

  3. Redis内存管理中的LRU算法

    在讨论Redis内存管理中的LRU算法之前,先简单说一下LRU算法: LRU算法:即Least Recently Used,表示最近最少使用页面置换算法.是为虚拟页式存储管理服务的,是根据页面调入内存 ...

  4. LRU 算法

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

  5. Redis内存回收:LRU算法

    Redis技术交流群481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read Redis中采用两种算法进行内存回收,引用计数算法以及LRU ...

  6. LRU算法原理解析

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

  7. 二叉树遍历问题、时间空间复杂度、淘汰策略算法、lru数据结构、动态规划贪心算法

    二叉树的前序遍历.中序遍历.后序遍历 前序遍历 遍历顺序规则为[根左右] ABCDEFGHK 中序遍历 遍历顺序规则为[左根右] BDCAEHGKF 后序遍历 遍历顺序规则为[左右根] DCBHKGF ...

  8. LRU算法详解

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

  9. Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?

    在<Redis 数据缓存满了怎么办?>我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据. 淘汰策略如下所示: 设置过期时间的 key volatile-ttl.vo ...

  10. 缓存淘汰算法--LRU算法

    1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...

随机推荐

  1. Process ProcessThread Thread

    Process ProcessThread: Process and ProcessThread objects have a ProcessorAffinity property of IntPtr ...

  2. django1.8模板位置的设置setting.py

    大多数django教程比较老,给出的template的设置方案为: 更改工程下的setting.py文件, TEMPLATE_DIRS = (     os.path.join( APP_DIR, ' ...

  3. iperf——网络性能测试工具

    一.前言 工作中遇到需要测试Linux服务器网卡占用率的场景,查阅资料后,发现iperf是一款合适的网络测速工具. 下面讲解一下如何使用iperf做网络性能测试. 二.基础知识 先补充一些基础知识: ...

  4. LIS问题(DP解法)---poj1631

    题目链接:http://poj.org/problem?id=1631 这个题题目有些难看懂hhh,但实质就是求LIS--longest increasing sequence. 以下介绍LIS的解法 ...

  5. UVa 11988 Broken Keyboard (a.k.a. Beiju Text)(链表)

    You're typing a long text with a broken keyboard. Well it's not so badly broken. The only problem wi ...

  6. 归并排序/合并排序c++实现

    #include <iostream>#include<string.h> using namespace std;class merges{public:void merge ...

  7. XAMPP下apache部署网站,多个虚拟机(空间)配置

     之 前在 Mac 下一直使用 MAMP 搭建本地 php 环境,因为其强大的组合配置以及方便的 UI 操作.因为公司的电脑是 Windows 的,有时候需要搭建 php 环境来调试一些小东西方便自己 ...

  8. CentOS 7安装Samba 4.6 版本步骤及错误解决方法

    首先通过这次教训,让我养成一个好习惯:备份  备份  备份      不管做什么配置或者更改什么东西之前先做好备份! 还有我本身的一个坏毛病:眼高手低! 工厂有一台服务器,由以前的运维装的Samba ...

  9. Apache Cordova vs Adobe PhoneGap: the differences and which one to use

    http://www.makehybridapps.com/2014/06/09/cordova-vs-phonegap-the-differences-and-which-one-to-use/

  10. Winform窗体控件级权限处理

    公共类: static class PowerHelper    {        /// <summary>         /// 设置form上的组件的权限         /// ...