LeetCode - LRU Cache

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值