Wednesday, February 4, 2015

LRU Cache -- LeetCode

[Question]

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.

[Analysis]
The LRU cache will remove the most aged item in the cache (http://en.wikipedia.org/wiki/Cache_algorithms).

There are many ways to implement LRU. The focus is to select the right data structure so that every get() and set() can be executed in constant time. Doubly linked list, which allows delete a node by the pointer pointing to the node, and Hashtable, which allows direct access to the node by given key, will give a constant-time operations to get() and set().

The data structure looks like:


[Solution]
class LRUCache{

    struct Node {
        int key;
        int val;
        Node* prev;
        Node* next;
        Node(int k=0, int v=0) : key(k), val(v), prev(NULL), next(NULL) {};
    };
 
    Node* cache_head;
    Node* cache_tail;
    unordered_map<int, Node*> map;
    int count;
    int limit;

    void insert_node(int key, int value) {
        if (map.find(key)!=map.end()) return;
     
        Node *node = new Node(key,value);
        map[key] = node;
        if (cache_head==NULL) {
            cache_head = cache_tail = node;
            count = 1;
        }
        else {
            node->next = cache_head;
            cache_head->prev = node;
            cache_head = node;
            count++;
        }
    }
 
    void remove_node(int key) {
        if (map.find(key)==map.end()) return;
        Node* node = map[key];
     
        if (cache_head == node) cache_head= node->next;
        if (cache_tail == node) cache_tail= node->prev;
     
        if (node->prev)
            node->prev->next = node->next;
        if (node->next)
            node->next->prev = node->prev;
        node->next = node->prev=NULL;
     
        delete node;
        map[key] = NULL;
        map.erase(key);
        count--;
    }

public:
    LRUCache(int capacity) : cache_head(NULL), cache_tail(NULL), count(0), limit(capacity) {
    }
 
    int get(int key) {
        if (map.find(key)==map.end())
            return -1;
     
        int value = map[key]->val;
        remove_node(key);
        insert_node(key, value);
        return value;
    }
 
    void set(int key, int value) {
        if (map.find(key) == map.end()) {
            if (count == limit)
                remove_node(cache_tail->key); //Remove the aged node from cache.
        }
        else {
            remove_node(key);
        }
        insert_node(key, value);      
    }
};

No comments:

Post a Comment