[Leetcode] LRU Cache. 哈希表+双向链表之实现

题目
首先来看题目,就是实现一个LRU,最近最少使用。

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()即是写。一次读或写意味着对缓存块使用了一次,该缓存的优先级就提高。

思路比较简单,基于哈希表和双向链表。先通过哈希表查找缓存块的位置,也就是缓存块在链表中的位置。然后把该缓存块置于链表表头。如果缓存已经满了,那就把表尾的缓存块丢掉,再在表头插入新的缓存块。

实现
用C++11实现的时候比较蛋疼,怪自己不够熟悉STL。 
这里用到连个容器:

std::list,相当于双向链表。splice(),push_front(),pop_back()的复杂度为O(1)O(1)。
std::unordered_map,相当于哈希表。find(),insert(),erase()的复杂度为O(1)O(1)(单个element)。
这里需要注意的地方有两点: 
1. 由于这里用了std::list,所以就不能用指针指向list中的element,要用iterator。值得注意的是,std::list的iterator是不会因为插入删除而改变所指向的element的,而std::vector的iterator是会改变所指向的element的(例如erase了之后)。 
所以在此哈希表可以这样实现: 
unordered_map<int, list<T>::iterator> cacheMap; 
2. std::list中,element的移动可以用splice()来实现: 
例如:

std::list<int> list = {1,2,3};
auto it = list.begin();
it = std::next(it); //it指向第二个element,2
if (it != list.begin())
{
    // 将list.begin()置于it之后
    list.splice(list.begin(), list, it, std::next(it)); // 2,1,3
}

My Solution

/*
 * LRUCache
 *
 * Using double linked list (stl list) and hash map(stl unordered_map)
 */

#include <iostream>
#include <unordered_map>
#include <memory>
#include <list>

using namespace std;

struct KeyVal
{
    int key;
    int value;
};

class LRUCache{
private:
    int capacity;           // cache capacity
    list<KeyVal> cache;     // cache, implement in Double-Linked-List
    unordered_map<int, list<KeyVal>::iterator> cacheMap;    // Hash Map, quickly access to value

public:
    LRUCache(int capacity):
        capacity(capacity) 
    { }

    int get(int key) {
        auto it = cacheMap.find(key);
        // Find by key
        if (it != cacheMap.end()) {
            //Find it! Get the value.
            auto kv = it->second;
            // Move to front
            if (kv != cache.begin())
                cache.splice(cache.begin(), cache, kv, next(kv));

            return kv->value;
        }

        return -1;
    }

    void set(int key, int value) {
        auto it = cacheMap.find(key);
        // Find by key
        if (it != cacheMap.end() ){
            // find and set new value
            auto kv = it->second;
            kv->value = value;
            // move front
            if (kv != cache.begin())
                cache.splice(cache.begin(), cache, kv, next(kv));
        }
        else {
            // Not found
            if (cacheMap.size() < capacity) {
                KeyVal newKv = {key, value};
                // set in cache
                cache.push_front(newKv);
                // add to map
                cacheMap.insert(make_pair(key, cache.begin()));
            }else {
                // Capacity is not enough
                // Delete the least used value
                int oldKey = cache.back().key;
                cache.pop_back();
                cacheMap.erase(oldKey);

                // Add new value
                KeyVal newKv = {key, value};
                cache.push_front(newKv);
                cacheMap.insert(make_pair(key, cache.begin()));
            }
        }
    }
};

之所以要用双向链表是因为只需要记录尾指针,查找,删除或插入尾节点的时间为O(1),单向链表为O(N),单链表只有后继指针,没有前驱指针,而双向链表包含前驱指针,故删除指针后,可直接将前驱结点和后继结点连起来
————————————————
版权声明:本文为CSDN博主「骚铭科技」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_21645561/article/details/50274835

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值