LRU Cache

LeetCode #146


Description:

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

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(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.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

Idea:

List存CachNode, unordered_map存(key,list里的position(iterator)

为了使查找、插入和删除都有较高的性能,我们使用一个双向链表 (std::list) 和一个哈希表
(std::unordered_map),因为:
• 哈希表保存每个节点的地址,可以基本保证在 O(1) 时间内查找节点
• 双向链表插入和删除效率高,单向链表插入和删除时,还要查找节点的前驱节点
具体实现细节:
• 越靠近链表头部,表示节点上次访问距离现在时间最短,尾部的节点表示最近访问最少
• 访问节点时,如果节点存在,把该节点交换到链表头部,同时更新 hash 表中该节点的地址
• 插入节点时,如果 cache 的 size 达到了上限 capacity,则删除尾部节点,同时要在 hash 表中删
除对应的项;新节点插入链表头部

entire list (1) 
void splice (iterator position, list& x);
single element (2)  
void splice (iterator position, list& x, iterator i);
element range (3)   
void splice (iterator position, list& x, iterator first, iterator last);

The first version (1) transfers all the elements of x into the container.
The second version (2) transfers only the element pointed by i from x into the container.
The third version (3) transfers the range [first,last) from x into the container.

Code:

Method 1
不用list splice, 效率不是很高,但是自己手写的容易懂。
更新的时候,直接删除,然后在list beginning插入。

class LRUCache {
public:
    LRUCache(int capacity) {
        capacity_ = capacity;
    }

    int get(int key) {
        if(cache_map_.find(key) == cache_map_.end()) return -1;
        int value=cache_map_[key]->value;
        cache_list_.erase(cache_map_[key]);
        cache_list_.insert(cache_list_.begin(), CacheNode(key, value));
        cache_map_[key]=cache_list_.begin();
        return value;
    }

    void put(int key, int value) {
        // Not exist, just add the new element
        if(cache_map_.find(key) != cache_map_.end()){
            cache_list_.erase(cache_map_[key]);
            cache_list_.insert(cache_list_.begin(), CacheNode(key, value));
            cache_map_[key]=cache_list_.begin();
        }
        // Already exist
        else{
            // Over capacity, need to delete the least frequenct element
            if(cache_list_.size() == capacity_){
                cache_map_.erase(cache_list_.back().key);
                cache_list_.pop_back();
            }

            // deal with both under capacity, and over capacity but after deletion
            cache_list_.push_front(CacheNode(key, value));
            cache_map_[key] = cache_list_.begin();
        }
    }

private:
    struct CacheNode{
        int key;
        int value;
        CacheNode(int k, int v):key(k), value(v){}

    };
    int capacity_;
    list<CacheNode> cache_list_;
    unordered_map<int, list<CacheNode>::iterator> cache_map_;

};

Method 2
用list splice

class LRUCache {
public:
    LRUCache(int capacity) {
        capacity_ = capacity;
    }

    int get(int key) {
        if(cache_map_.find(key) == cache_map_.end()) return -1;
        auto it = cache_map_[key];
        cache_list_.splice(cache_list_.begin(), cache_list_, it);
        // cache_map_[key] = cache_list_.begin(); // list splice does not change iterator
        return cache_map_[key]->value;
    }

    void put(int key, int value) {

        // Already exist, just add the new element
        if(cache_map_.find(key) != cache_map_.end()){
            cache_map_[key]->value = value;
            cache_list_.splice(cache_list_.begin(), cache_list_, cache_map_[key]);
            // cache_map_[key] = cache_list_.begin();  // list splice does not change iterator
        }
        // Not exist
        else{
            // Over capacity, need to delete the least frequenct element
            if(cache_list_.size() == capacity_){
                cache_map_.erase(cache_list_.back().key);
                cache_list_.pop_back();
            }

            // deal with both under capacity, and over capacity but after deletion
            cache_list_.push_front(CacheNode(key, value));
            cache_map_[key] = cache_list_.begin();

        }

    }

private:
    struct CacheNode{
        int key;
        int value;
        CacheNode(int k, int v):key(k), value(v){}

    };
    int capacity_;
    list<CacheNode> cache_list_;
    unordered_map<int, list<CacheNode>::iterator> cache_map_;

};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

results matching ""

    No results matching ""