LeetCode146. LRU缓存(C++)

题目地址:力扣

解法1:使用自己写的链表和自己写的哈希表来实现

不太优雅的解法,因为没用STL提供的Hashmap,所以这里手动创建一个Hashmap类来模拟哈希表,因此占用空间比较多。题目设计缓存的容量最大就是10001,因此就开一个这么大的数组用于模拟哈希表。

// account 用于记录链表中当前有多少节点(哈希表中有多少个位置是存了元素的)
int account = 0;

/* 链表节点定义
 * key对应哈希表的key
 * pre是指向前面节点的指针
 * next是指向后面节点的指针
 */

class LinkListNode
{
public:
    LinkListNode(int pkey = -1) : key(pkey), pre(nullptr), next(nullptr)  {}
    int key;
    LinkListNode* pre;
    LinkListNode* next;
};

/* 哈希表定义
 * exist标记当前key是否存在,存在为0,不存在为-1
 * 当前key对应的value
 * pointer指向当前key对应链表中的节点
 */

class Hashmap
{
public:
    Hashmap() : exist(-1), value(0), pointer(nullptr) {}
    int exist;
    int value;
    LinkListNode* pointer;
};

/* 链表定义
 * 带有头结点head和尾节点tail的双链表
 * insert是头插法插入链表
 * pop从尾部弹出节点
 * insidePop对应是题中的get操作,从链表中间取出一个元素
 * 
 */

class LinkList
{
public:
    LinkList() { head->next = tail; tail->pre = head;}
    void insert(LinkListNode* newNode);
    void pop(Hashmap hashmap[]);
    void insidePop(Hashmap hashmap[], int key);
    LinkListNode* head = new LinkListNode();
    LinkListNode* tail = new LinkListNode();
};

// 链表头插操作

void LinkList::insert(LinkListNode* newNode)
{
    newNode->next = head->next;
    head->next->pre = newNode;
    newNode->pre = head;
    head->next = newNode;
}

// 链表尾弹出元素操作

void LinkList::pop(Hashmap hashmap[])
{
    // 链表操作
    LinkListNode *tmp = tail->pre;
    // cout << tmp->key << "被逐出了" << endl;
    tail->pre = tail->pre->pre;
    tail->pre->next = tail;
    --account;
    // 哈希表操作
    hashmap[tmp->key].exist = -1;
    hashmap[tmp->key].pointer = NULL;
    delete(tmp);
}

// 链表中间弹出元素操作

void LinkList::insidePop(Hashmap hashmap[], int key)
{
    LinkListNode *tmp = hashmap[key].pointer;
    tmp->pre->next = tmp->next;
    tmp->next->pre = tmp->pre;
}


class LRUCache {
public:
    LRUCache(int capacity) {    this->capacity = capacity;
    }
    
    int get(int key) {
        if (hashmap[key].exist != 0)
            return -1;
        else
            list.insidePop(hashmap, key);    
            list.insert(hashmap[key].pointer);    // 从链表中间取出节点
            return hashmap[key].value;    // 将节点插入链表头部
    }
    
    void put(int key, int value) {
        if (hashmap[key].exist != -1) // 哈希表中对应元素存在
        {
            hashmap[key].value = value;   //  如果这个key存在则赋新值
            list.insidePop(hashmap, key);    // 从链表中间取出节点
            list.insert(hashmap[key].pointer);    // 将节点插入链表头部
        }
        else {
            if ( account == capacity )    // 若缓存已满
                list.pop(hashmap);    // 弹出尾部元素
            hashmap[key].exist = 0;
            hashmap[key].value = value;
            LinkListNode *newNode = new LinkListNode(key);
            list.insert(newNode);
            hashmap[key].pointer = newNode;
            ++account;
        }
    }
private:
    Hashmap hashmap[10001] ;
    LinkList list;
    int capacity;
};

解法2:使用STL的链表和哈希表来完成

本来是可以用单链表就解决问题的,但是STL提供的forward_list并没有暴露其指针的接口,这就导致了我们无法移动元素,而只能先删除元素再进行插入来替代移动元素,同时forward_list不支持删除当前迭代器指向的元素,只支持删除当前迭代器之后的元素,所以这里最终采用双链表来完成。

其中使用到了双链表的pop_back, emplace_front, splice(搬动元素)等函数

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

    int get(int key) {
        // 若哈希表中没有key则返回-1
        if (cachemap.find(key) == cachemap.end())
            return -1;
        // 有则更新key所在元素的位置
        cachelist.splice(cachelist.begin(), cachelist, cachemap[key]);
        cachemap[key] = cachelist.begin();
        return (*cachemap[key]).second;
    }
    
    void put(int key, int value) {
        // 若缓存中有这个元素,即哈希表中有key,则更新哈希表与链表
        if (cachemap.find(key) != cachemap.end())
        {
            (*cachemap[key]).second = value;
            cachelist.splice(cachelist.begin(), cachelist, cachemap[key]);
            cachemap[key] = cachelist.begin();
            // update(key);
        // 若缓存中没有这个元素,且插入操作会使得缓存爆了,则删除末尾的元素。然后插入
        } else if (cachemap.size() == cap)
        {
            cachemap.erase(cachelist.back().first);
            cachelist.pop_back();
            cachelist.emplace_front(key, value);
            cachemap[key] = cachelist.begin();
        // 否则直接插入该元素
        } else {
            cachelist.emplace_front(key, value);
            cachemap[key] = cachelist.begin();
        }
    }

private:
    // 定义哈希表和双链表
    unordered_map<int, list<pair<int, int>>::iterator> cachemap;
    list<pair<int, int>> cachelist;
    // 定义容量
    int cap;
};

解法3:使用STL的哈希表和自己定义的链表来完成

注意用自己定义的链表,new了在用不到以后要delete来释放内存。那么LRUCache类就需要析构函数

class listNode{
public:
    listNode(int k, int v, listNode *p = nullptr, listNode *n = nullptr) : key(k), val(v), pre(p), next(n)  {}
    int val;
    int key;
    listNode* pre;
    listNode* next;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
};

class LRUCache {
public:
    LRUCache(int capacity) {
        cap = capacity;
        head = new listNode(-1, -1);
        tail = new listNode(-1, -1);
        head->next = tail;
        tail->pre = head;
    }
    
    ~LRUCache() {
        while (head != nullptr)
        {
            listNode* tmp = head;
            head = head->next;
            delete(tmp);
        }
    }

    int get(int key) {
        if (cachemap.find(key) == cachemap.end())
            return -1;
        // 更新节点位置
        cachemap[key]->pre->next = cachemap[key]->next;
        cachemap[key]->next->pre = cachemap[key]->pre;
        cachemap[key]->next = head->next;
        head->next = cachemap[key];
        cachemap[key]->pre = head;
        cachemap[key]->next->pre = cachemap[key];
        return cachemap[key]->val;
    }

    void put(int key, int value) {
        // 若缓存中有这个元素,即哈希表中有key,则更新哈希表与链表
        if (cachemap.find(key) != cachemap.end())
        {
            cachemap[key]->val = value;
            cachemap[key]->pre->next = cachemap[key]->next;
            cachemap[key]->next->pre = cachemap[key]->pre;
            cachemap[key]->next = head->next;
            head->next = cachemap[key];
            cachemap[key]->pre = head;
            cachemap[key]->next->pre = cachemap[key];
        // 若缓存中没有这个元素,且插入操作会使得缓存爆了,则删除末尾的元素。然后插入
        } else {
            if (cachemap.size() == cap)
            {
                listNode* tmp = tail->pre;
                cachemap.erase(tail->pre->key);
                tail->pre->pre->next = tail;
                tail->pre = tail->pre->pre;
                delete(tmp);
            }
            // 插入新节点
            listNode *newnode = new listNode(key, value, head, head->next);
            cachemap[key] = newnode;
            newnode->next->pre = newnode;
            head->next = newnode;
        }
    }
private:
    unordered_map<int, listNode*> cachemap;
    listNode* head;
    listNode* tail;
    int cap;
};

Accepted

  • 22/22 cases passed (400 ms)
  • Your runtime beats 43.15 % of cpp submissions
  • Your memory usage beats 35.56 % of cpp submissions (161.2 MB)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值