LRU Cache

Problem:

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.

set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before.

分析:

先用例子回顾LRU调度算法,假设内存中页面容量是3,而共有6页,编号依次为1至6。现在有十次页面调用,依次为1, 3, 1, 2, 4, 5, 3, 4, 6, 1。首先假设内存中空,内存中页面的替换顺序为

(1) 1

(2) 3, 1

(3) 1, 3

(4) 2, 1, 3

(5) 4, 2, 1

(6) 5, 4, 2

(7) 3, 5, 4

(8) 4, 3, 5

(9) 6, 4, 3

(10) 1, 6, 4

十次页面调用中,除去第(3)(8)两次外,均发生了缺页中断。观察以上的序列调用情况,整体上页面被组织成了一个队列形式,刚刚使用的页面放在队尾,而每次被替换出去的页面都放在队头。如果没有缺页中断,页面在内存中,也要将刚刚访问的页面放在队尾。在内存容量没有到达前,所有的缺页中断均将页面直接接在队尾。

以上的分析能够看出,页面的替换算法中涉及了很多的元素顺序调整,也就是队列中的移除与插入操作,所以使用链表可以降低时间开销。然而,检测访问页面是否在内存中是较耗时的操作,为了加速访问效率,可以用map加速查找。

双向队列可以方便删除与插入操作,使用哨兵能够让代码变得简洁,所以综上,采用带哨兵的双向链表与map结合,能够达到较好的代码编写难度与效率的平衡。map使用了红黑树的结构,可以保证get与set操作的平均时间复杂度在O(log n)。

 1 struct CacheBlock {
 2     int key;
 3     int value;
 4     CacheBlock *prev;
 5     CacheBlock *next;
 6     
 7     CacheBlock(int k, int v) : key(k), value(v), prev(NULL), next(NULL) {}
 8 };
 9 
10 class LRUCache{
11     CacheBlock *list; // doubly list with sentinel
12     int capacity;
13     int size;
14     map<int, CacheBlock*> caches;
15     
16 public:
17     LRUCache(int cap) : list(NULL), capacity(cap), size(0) {
18         // initialize sentinel
19         list = new CacheBlock(-1, -1);
20         list->prev = list->next = list;
21     }
22     
23     ~LRUCache() {
24         if(list) {
25             delete list;
26             list = NULL;
27         }
28     }
29     
30     int get(int key) {
31         if(caches.find(key) == caches.end())
32             return -1;
33         else
34             return caches[key]->value;
35     }
36     
37     void set(int key, int value) {
38         if(caches.find(key) == caches.end()) { // page fault
39             if(size < capacity) {
40                 // add head
41                 CacheBlock *cb = new CacheBlock(key, value);
42                 insert(cb);
43                 caches[key] = cb;
44                 ++size;
45             } else {
46                 // remove tail
47                 CacheBlock *tail = list->prev;
48                 remove(tail);
49                 caches.erase(tail->key);
50                 // add head
51                 tail->key = key;
52                 tail->value = value;
53                 tail->next = tail->prev = NULL;
54                 insert(tail);
55                 caches[key] = tail;
56             }
57         } else {
58             // find page
59             CacheBlock *cb = caches[key];
60             // remove page
61             remove(cb);
62             // add head
63             cb->value = value;
64             cb->next = cb->prev = NULL;
65             insert(cb);
66         }
67     }
68     
69     void insert(CacheBlock *cb) {
70         // insert to front of the list
71         cb->next = list->next;
72         list->next->prev = cb;
73         list->next = cb;
74         cb->prev = list;
75     }
76     
77     void remove(CacheBlock *cb) {
78         cb->prev->next = cb->next;
79         cb->next->prev = cb->prev;
80     }
81 };

 

posted @ 2014-04-28 15:02  夏目家的猫咪老师  阅读(255)  评论(0编辑  收藏  举报