LeetCode146:LRU Cache

题目:

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 inserting a new item.

解题思路:

利用双向链表+hashtable实现

Cache中的存储空间往往是有限的,当Cache中的存储块被用完,而需要把新的数据Load进Cache的时候,我们就需要设计一种良好的算法来完成数据块的替换。LRU的思想是基于“最近用到的数据被重用的概率比较早用到的大的多”这个设计规则来实现的。

为了能够快速删除最久没有访问的数据项和插入最新的数据项,我们双向链表连接Cache中的数据项,并且保证链表维持数据项从最近访问到最旧访问的顺序。每次数据项被查询到时,都将此数据项移动到链表头部(O(1)的时间复杂度)。这样,在进行过多次查找操作后,最近被使用过的内容就向链表的头移动,而没有被使用的内容就向链表的后面移动。当需要替换时,链表最后的位置就是最近最少被使用的数据项,我们只需要将最新的数据项放在链表头部,当Cache满时,淘汰链表最后的位置就是了。
    查找一个链表中元素的时间复杂度是O(n),每次命中的时候,我们就需要花费O(n)的时间来进行查找,怎么样才能提高查找的效率呢?当然是hashtable了,因为它的查找时间复杂度是O(1)。

实现代码:

#include <iostream>
#include <unordered_map>

using namespace std;
/*
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 inserting a new item.
*/

//双向链表节点 
struct LRUNode
{
    int key;
    int val;
    LRUNode *pre;
    LRUNode *next;
    LRUNode(int k = 0, int v = 0):key(k), val(v),pre(NULL), next(NULL){
    } 
};
class LRUCache{
public:
    LRUCache(int capacity):cap(capacity),size(0) {
        head = new LRUNode();
        tail = new LRUNode();
        head->next = tail;
        tail->pre = head;
             
    }
    
    int get(int key) {
        LRUNode *t = hashtable[key];
        if(t)//key在hashtable存在,则调整该key在链表中对应节点的位置,将其插入到最前面 
        {
            //分离t节点 
            t->pre->next = t->next;
            t->next->pre = t->pre;
            //将t节点插入到头结点之后,即第一个数据节点 
            t->pre = head;
            t->next = head->next;
            head->next = t;
            t->next->pre = t;
            return t->val;
            
        }
        else
            return -1;
        
    }
    
    void set(int key, int value) {
        LRUNode *t = hashtable[key];
        if(t)//key在hashtable存在,则更新value及调整该key对应节点在链表中位置,将其插入到第一个节点 
        {
            t->val = value;
            //分离t节点 
            t->pre->next = t->next;
            t->next->pre = t->pre;
            //将t节点插入到头结点之后,即第一个数据节点
            t->pre = head;
            t->next = head->next;
            head->next = t;
            t->next->pre = t;
            return ;
            
        }

        if(size == cap)//如果双向链表容量已满即缓存容量已满,则将最近不使用的节点即链表最后一个数据节点删除 
        {
            LRUNode *tmp = tail->pre;
            tail->pre->pre->next = tail;           
            tail->pre = tmp->pre;
            hashtable.erase(tmp->key);
            delete tmp;
            size--;
        }
        
        //创建key对应的一个新节点,插入到最前面 
        LRUNode *node = new LRUNode(key, value);
        
        node->pre = head;
        node->next = head->next;
        head->next = node;
        node->next->pre = node;
        
        hashtable[key] = node;//在hashtable添加key对应的表项 
        
        size++;//链表节点数++ 
        
        
    }
private:
    int cap;//链表容量即缓存容量 
    int size;//缓存当前使用量 
    LRUNode *head;//链表头结点,不存数据, 
    LRUNode *tail;//链表尾节点,不存数据 
    unordered_map<int,LRUNode*> hashtable;//hashtable,用作查找O(1)时间复杂度 
    
};
int main(void)
{
    LRUCache lrucache(3);
    lrucache.set(1,8);
    lrucache.set(2,9);
    lrucache.set(3,10);
    lrucache.set(4,11);
    cout<<lrucache.get(1)<<endl;
    cout<<lrucache.get(2)<<endl;
    cout<<lrucache.get(4)<<endl;

    
    return 0;
}
posted @ 2014-02-17 12:55  mickole  阅读(363)  评论(0编辑  收藏  举报