【算法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++代码如下:

复制代码
  1 /**
  2  * LRU Cache Implementation using DoubleLinkList & hashtable
  3  * Copyright 2015 python27
  4  * 2015/06/26
  5  */
  6 #include <iostream>
  7 #include <string>
  8 #include <map>
  9 #include <list>
 10 #include <deque>
 11 #include <cassert>
 12 #include <cstdio>
 13 #include <cstdlib>
 14 using namespace std;
 15 
 16 struct CacheNode
 17 {
 18     int key;
 19     int value;
 20     CacheNode* prev;
 21     CacheNode* next;
 22     
 23     CacheNode(int k, int v) : key(k), value(v), prev(NULL), next(NULL)
 24     {}
 25     
 26     CacheNode():key(0), value(0), prev(NULL), next(NULL)
 27     {}        
 28 };
 29 
 30 class LRUCache
 31 {
 32 public:
 33     LRUCache(int capacity);
 34     
 35     int Get(int key);
 36     void Put(int key, int value);
 37     
 38 public:
 39     void PrintList() const;
 40 private:
 41     void InsertNodeFront(CacheNode* p);
 42     void DeleteNode(CacheNode* p);
 43     
 44 private:
 45     map<int, CacheNode*> m_hashtable;        // hash table
 46     CacheNode* m_head;                        // double link list head
 47     CacheNode* m_tail;                        // double link list tail
 48     int m_capacity;                            // capacity of link list
 49     int m_size;                                // current size of link list
 50 };
 51 
 52 void LRUCache::PrintList() const
 53 {
 54     CacheNode* p = m_head;
 55     for (p = m_head; p != NULL; p = p->next)
 56     {
 57         printf("(%d, %d)->", p->key, p->value);
 58     }
 59     printf("\n");
 60     printf("size = %d\n", m_size);
 61     printf("capacity = %d\n", m_capacity);
 62 }
 63 
 64 LRUCache::LRUCache(int capacity)
 65 {
 66     m_capacity = capacity;
 67     m_size = 0;
 68     m_head = NULL;
 69     m_tail = NULL;
 70 }
 71 
 72 //    insert node into head pointed by p
 73 void LRUCache::InsertNodeFront(CacheNode* p)
 74 {
 75     if (p == NULL) return;
 76     
 77     if (m_head == NULL)
 78     {
 79         m_head = p;
 80         m_tail = p;
 81     }
 82     else
 83     {        
 84         p->next = m_head;
 85         m_head->prev = p;
 86         m_head = p;
 87     }
 88 }
 89 
 90 // delete node in double linklist pointed by p
 91 void LRUCache::DeleteNode(CacheNode* p)
 92 {
 93     if (p == NULL) return;
 94     
 95     assert(m_head != NULL && m_tail != NULL);
 96     
 97     if (m_size == 1)
 98     {
 99         if (p == m_head && p == m_tail)
100         {
101             delete p;
102             m_head = NULL;
103             m_tail = NULL;
104         }
105         else
106         {
107             fprintf(stderr, "Delete Wrong! No such Node");
108             return;
109         }
110     }
111     else if (p == m_head)
112     {
113         m_head = m_head->next;
114         m_head->prev = NULL;
115         delete p;
116     }
117     else if (p == m_tail)
118     {
119         m_tail = m_tail->prev;
120         m_tail->next = NULL;
121         delete p;
122     }
123     else
124     {
125         p->prev->next = p->next;
126         p->next->prev = p->prev;
127         delete p;
128     }
129     
130 }
131 
132 int LRUCache::Get(int key)
133 {
134     // if key not in return -1
135     if (m_hashtable.find(key) == m_hashtable.end())
136     {
137         return -1;
138     }
139         
140     CacheNode* p = m_hashtable[key];
141     int k = p->key;
142     int v = p->value;
143     
144     // delete this node
145     DeleteNode(p);
146         
147     // insert this node to the head
148     p = new CacheNode(k, v);
149     InsertNodeFront(p);
150     // update hash table
151     m_hashtable[k] = p;
152     return p->value;
153 }
154 
155 void LRUCache::Put(int key, int value)
156 {
157     // if key alread in, update
158     if (m_hashtable.find(key) != m_hashtable.end())
159     {
160         CacheNode* p = m_hashtable[key];
161         
162         // delete node
163         DeleteNode(p);
164         // insert node
165         p = new CacheNode(key, value);
166         InsertNodeFront(p);
167         // update hash table
168         m_hashtable[key] = p;
169         return;
170     }
171     // if list is full, delete the tail node
172     else if (m_size >= m_capacity)
173     {
174         // delete the tail node
175         CacheNode* p = m_tail;
176         m_hashtable.erase(p->key);
177         DeleteNode(p);
178         m_size--;
179     }
180     
181     // create node and insert into head
182     assert(m_size < m_capacity);
183     CacheNode* p = new CacheNode(key, value);
184     InsertNodeFront(p);
185     m_hashtable[key] = p;
186     m_size++;
187 }
188 
189 int main()
190 {
191     LRUCache lru(3);
192     lru.Put(1, 11);
193     lru.PrintList();
194     lru.Put(2, 22);
195     lru.PrintList();
196     lru.Put(3, 33);
197     lru.PrintList();
198     lru.Put(4, 44);
199     lru.PrintList();
200     int value = lru.Get(3);
201     printf("Get(3) = %d\n", value);
202     lru.PrintList();
203     value = lru.Get(2);
204     printf("Get(2) = %d\n", value);
205     lru.PrintList();
206     value = lru.Get(4);
207     printf("Get(4) = %d\n", value);
208     lru.PrintList();
209     value = lru.Get(1);
210     printf("Get(1) = %d\n", value);
211     lru.PrintList();
212     
213     return 0;
214 }
复制代码

 

posted @   python27  阅读(2972)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示