LeetCode 146. LRU缓存机制

题目描述:

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

思路:这里采用的是使用双向链表和hash_map结合的思想,hash_map可以保证查找的时间复杂度是O(1),双向链表可以满足增删的时间复杂度是O(1)。因此我们只需要采用一种很好的结构,使得,我们在知道key的时候,就知道其对应的节点,从而做出修改。

首先我们定义的双向链表的节点如下:

struct linkNode{
    int key;
    int val;
    linkNode *next, *pre;
    linkNode(int k, int v): key(k), val(v), next(NULL), pre(NULL) {};
};

然后我们用一个定义一个哈希表,把对应的key和链表的节点映射出来。

对于最近最少的数据,我们可以采用这样的机制,我们用双向链表表示一个数据表,靠近链表尾部的节点,说明该节点距离上次活动的时间越近,上次活动可以是插入,也可以是获取。知道了这个基本思路之后,我们的具体步骤如下:

1. 当get数据的时候,首先判断这个数据是否在链表中(通过哈希表实现),如果在,则返回这个数据,并且把这个数据调整到链表的末尾,这个是通过结构中的指针实现;如果不在则返回-1

2. 当put数据的时候,首先判断这个数据是否在链表中,如果在,那么则更新这个数据的value值,并且把该节点移到末尾;如果不在链表中,先判断链表现在是否已经满了,如果满了,则删除链表头部的数据,在链表末尾处加入新的数据,同时删除在哈希表中对应的key,如果没有满的话,就加入到链表尾。再把这个节点加入到哈希表中。

代码:

struct linkNode{//双向链表,需要在尾部插入
    int key;
    int val;
    linkNode *next, *pre;
    linkNode(int k, int v): key(k), val(v), next(NULL), pre(NULL) {};
};
 
class LRUCache {
public:
    LRUCache(int capacity) {
        cap = capacity;
        link = new linkNode(-1, -1);
        end = link;
    }
    
    linkNode * adjestLink(linkNode *head){
        if(head->next != NULL){
            head->pre->next = head->next;
            head->pre->next->pre = head->pre;
            head->next = NULL;
            end->next = head;
            head->pre = end;
            end = end->next;
        }
        //else说明本来就在末尾处,不需要修改
        return head;
 
    }
    
    int get(int key) {
        if(key2value.find(key) != key2value.end()){//说明原来的map里面有这个数,需要调整链表
            linkNode *p = key2value[key];//真正的头指针
            key2value[key] = adjestLink(p);
            return key2value[key]->val;
        }
        return -1;
    }
    
    void put(int key, int value) {
        if(key2value.find(key) != key2value.end()){//说明原来的map里面有这个数,需要调整链表以及修改hash_map
            linkNode *p = key2value[key];//真正的头指针
            key2value[key] = adjestLink(p);
            key2value[key]->val=value;
        }
        else{//说明可以直接插入,需要判断是否链表已经满了
            if(key2value.size() == cap){
                linkNode *p = link->next;//真正的头指针
                int delKey = p->key;
                if(p->next==NULL){
                    p = new linkNode(key, value);
                    end->pre->next = p;
                    p->pre = end->pre;
                    end = p;
                }
                else{
                    p->pre->next = p->next;
                    p->pre->next->pre = p->pre;
                    free(p);
                    p = new linkNode(key, value);
                    end->next = p;
                    p->pre = end;
                    end = end->next;
                }
                
                key2value.erase(delKey);
                key2value[key] = p;
            }
            else{
                linkNode *p = new linkNode(key, value);
                end->next = p;
                p->pre = end;
                end = end->next;
                key2value[key] = p;
            }
        }
    }
private:
    int cap;
    unordered_map<int, linkNode *> key2value;
    linkNode *link;
    linkNode *end;
};

猜你喜欢

转载自blog.csdn.net/sinat_15723179/article/details/81248259