LeetCode - LRU Cache

来源:互联网 发布:蜂窝移动数据用量清零 编辑:程序博客网 时间:2024/06/05 20:54

https://leetcode.com/problems/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.

这道题是比较典型的设计题,为了让get和set都能在O(1)中间完成,需要用一个hashtable来记录cache中的<key, Node>对,这样可以使查找在O(1)时间内完成,另外,在找到node后,为了让删除Node也在O(1)完成,应该用double-linked list,这样通过prev, next指针,就可以直接删除List中的节点了。

需要注意的是,在get和set操作后,被操作的节点就应该是最新用过的节点,应该移到最后面。

另外,需要注意linked list的操作,比如删除头节点,尾节点,capacity只有1这种情况都应该考虑到。

public class LRUCache {    private int capacity;    private int size;    private Node first;    private Node last;    private HashMap<Integer, Node> map;        class Node{        int key;        int value;        Node prev, next;        public Node(int key, int val){            this.key = key;            this.value = val;            prev = null;            next = null;        }            }        public LRUCache(int capacity) {        this.map = new HashMap<Integer, Node>();        this.capacity = capacity;        this.size = 0;    }        public int get(int key) {        if(map.containsKey(key)){            Node current = map.get(key);            if(current!=last){                if(current==first){                    first = first.next;                    first.prev = null;                }                else{                    current.next.prev = current.prev;                    current.prev.next = current.next;                }                last.next = current;                current.prev = last;                current.next = null;                last = current;            }            return current.value;        }        return -1;    }        public void set(int key, int value) {        if(map.containsKey(key)){            Node current = map.get(key);            if(current!=last){                if(current==first){                    first = first.next;                    if(first!=null) first.prev = null;                }                else{                    current.next.prev = current.prev;                    current.prev.next = current.next;                }                last.next = current;                current.prev = last;                current.next = null;                last = current;            }            current.value = value;        }        else{            Node current = new Node(key, value);            map.put(key, current);            if(size<capacity){                                if(first==null){                    first = current;                    last = current;                }                else{                    current.prev = last;                    last.next = current;                    last = last.next;                }                size++;                            }            else{                Node tmp = first;                first = first.next;                if(first!=null) first.prev = null;                if(first==null){                    first = current;                    last = current;                }                else{                    current.prev = last;                    last.next = current;                    last = last.next;                }                map.remove(tmp.key);            }        }            }}

上面的代码有很多重复代码,比如删除节点之类的,所以,可以有两个remove和add函数,remove函数删除任意一个节点,add函数把一个节点加到list的末尾,两个函数都相应地更新size和hashmap。代码如下:

public class LRUCache {    private int capacity;    private int size;    private Node first;    private Node last;    private HashMap<Integer, Node> map;        class Node{        int key;        int value;        Node prev, next;        public Node(int key, int val){            this.key = key;            this.value = val;            prev = null;            next = null;        }            }        public LRUCache(int capacity) {        this.map = new HashMap<Integer, Node>();        this.capacity = capacity;        this.size = 0;    }        public void remove(Node node){        if(node==first){            first = first.next;            if(first!=null) first.prev = null;            else last=null;        }        else if(node == last){            last = last.prev;            if(last!=null) last.next = null;            else first = null;        }        else{            node.prev.next = node.next;            node.next.prev = node.prev;        }        map.remove(node.key);        size--;    }        public void add(Node node){        if(first==null || last==null){            last = node;            first = node;        }        else{            last.next = node;            node.prev = last;            last = last.next;        }        map.put(node.key, node);        size++;    }        public int get(int key) {        if(map.containsKey(key)){            Node current = map.get(key);            if(current!=last){                remove(current);                add(current);            }            return current.value;        }        return -1;    }        public void set(int key, int value) {        if(map.containsKey(key)){            Node current = map.get(key);            if(current!=last){                remove(current);                add(current);            }            current.value = value;        }        else{            Node current = new Node(key, value);            if(size>=capacity) remove(first);            add(current);        }    }}

以下是C++的版本:

第一个版本是自己定义list,自己定义list node,然后用unordered_map做的,注意,这里所有在JAVA里面直接保存的node引用都要变成node指针。即全部是指针操作了, 并且在这里所有的node都是用new定义的,不然node的有效范围就只是当前函数模块,但是用node定义的话,在彻底删除节点时,需要delete这个node:

class Node{public:    int key;    int value;    Node* prev;    Node* next;    Node(int k, int v){        this->key = k;        this->value = v;        prev = NULL;        next = NULL;    }};class LRUCache{private:    unordered_map<int, Node*> map;    Node* first;    Node* last;    int capacity;    int size;public:    LRUCache(int capacity) {        first = NULL;        last = NULL;        this->capacity = capacity;        this->size = 0;    }        void removeNode(Node* node){        if(node==first){            first = first->next;            if(first!=NULL) first->prev = NULL;            if(first==NULL) last = NULL;        }        else if(node==last){            last = last->prev;            if(last!=NULL) last->next = NULL;            if(last==NULL) first = NULL;        }        else{            node->prev->next = node->next;            node->next->prev = node->prev;        }        size--;        map.erase(node->key);    }        void add(Node* node){        if(first == NULL){            first = node;            last = node;        }        else{            last->next = node;            node->prev = last;            last = last->next;        }        size++;        map[node->key] = node;    }        int get(int key) {        if(map.find(key)!=map.end()){            Node* current = map[key];            removeNode(current);            add(current);            return current->value;        }        else return -1;    }        void set(int key, int value) {        if(map.find(key)!=map.end()){            Node* current = map[key];            removeNode(current);            add(current);            current->value = value;        }        else{            if(size==capacity){                Node* tdl = first;                removeNode(tdl);                delete tdl;            }            Node* current = new Node(key, value);            add(current);        }    }};

第二个版本是用STL里面的list做的,因为STL里面list是双向链表,forward_list是单向链表,所以不用自己定义list,直接用STL提供的模板做,注意iterator是指向模板内节点的指针:

class Node{public:    int key;    int value;    Node(int k, int v) : key(k), value(v){}};class LRUCache{private:    unordered_map<int, list<Node>::iterator> map;    list<Node> cache;    int capacity;public:    LRUCache(int capacity) {        this->capacity = capacity;    }        void MoveToHead(int key){        Node update = *map[key];        cache.erase(map[key]);        cache.push_front(update);        map[key] = cache.begin();    }        int get(int key) {        if(map.find(key)!=map.end()){            MoveToHead(key);            return map[key]->value;        }        else return -1;    }        void set(int key, int value) {        if(map.find(key)!=map.end()){            MoveToHead(key);            map[key]->value = value;        }        else{            if(cache.size()==capacity){                map.erase(cache.back().key);                cache.pop_back();            }            Node current(key, value);            cache.push_front(current);            map[key] = cache.begin();        }    }};




0 0