设计一个LRU Cache

原来遇到的一个面试题,今天意外想起,记录一下。


问题:设计实现一个LRU Cache,需要支持三种操作:插入(insert)、替换(replace)、查找(lookup)

思路:用HashMap做为基础容器,容器内Value组成双向链表,由双向链表支持LRU的替换策略。

链表需要维持数据项从最近访问到最旧访问的顺序。

  • 插入:当Cache未满时,新的数据项只需插到双链表头部即可。时间复杂度为O(1)O(1).

  • 替换:当Cache已满时,将新的数据项插到双链表头部,并删除双链表的尾结点即可。时间复杂度为O(1)O(1).

  • 查找:每次数据项被查询到时,都将此数据项移动到链表头部。

实现:

Java实现非常简单,LinkedHashMap自身已经实现了顺序存储,默认情况下是按照元素的添加顺序存储,也可以启用按照访问顺序存储,即最近读取的数据放在最前面,最早读取的数据放在最后面,然后它还有一个判断是否删除最老数据的方法,默认是返回false,即不删除数据,我们使用LinkedHashMap实现LRU缓存的方法就是对LinkedHashMap实现简单的扩展。



public static class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {  
  
        /** serialVersionUID */  
        private static final long serialVersionUID = -5933045562735378538L;  
  
        /** 最大数据存储容量 */  
        private static final int  LRU_MAX_CAPACITY     = 1024;  
  
        /** 存储数据容量  */  
        private int               capacity;  
  
        /** 
         * 默认构造方法 
         */  
        public LRULinkedHashMap() {  
            super();  
        }  
  
        /** 
         * 带参数构造方法 
         * @param initialCapacity   容量 
         * @param loadFactor        装载因子 
         * @param isLRU             是否使用lru算法,true:使用(按方案顺序排序);false:不使用(按存储顺序排序) 
         */  
        public LRULinkedHashMap(int initialCapacity, float loadFactor, boolean isLRU) {  
            super(initialCapacity, loadFactor, true);  
            capacity = LRU_MAX_CAPACITY;  
        }  
  
        /** 
         * 带参数构造方法 
         * @param initialCapacity   容量 
         * @param loadFactor        装载因子 
         * @param isLRU             是否使用lru算法,true:使用(按方案顺序排序);false:不使用(按存储顺序排序) 
         * @param lruCapacity       lru存储数据容量        
         */  
        public LRULinkedHashMap(int initialCapacity, float loadFactor, boolean isLRU, int lruCapacity) {  
            super(initialCapacity, loadFactor, true);  
            this.capacity = lruCapacity;  
        }  
  
        /**  
         * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry) 
         */  
        @Override  
        protected boolean removeEldestEntry(Entry<K, V> eldest) {  
            System.out.println(eldest.getKey() + "=" + eldest.getValue());  
              
            if(size() > capacity) {  
                return true;  
            }  
            return false;  
        }  
    } 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LRU 缓存是一种常见的缓存淘汰算法,它的设计思想是将最近最少使用的数据从缓存中淘汰出去,以保证缓存的数据都是热点数据,从而提高缓存的命中率。 下面是一个基于双向链表和哈希表实现的 LRU 缓存的 C++ 设计实现: ```cpp #include <unordered_map> struct Node { int key; int value; Node* prev; Node* next; Node(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {} }; class LRUCache { public: LRUCache(int capacity) : size_(capacity), head_(nullptr), tail_(nullptr) {} int get(int key) { auto iter = cache_.find(key); if (iter == cache_.end()) { return -1; } else { Node* node = iter->second; move_to_head(node); return node->value; } } void put(int key, int value) { auto iter = cache_.find(key); if (iter == cache_.end()) { Node* node = new Node(key, value); add_to_head(node); cache_[key] = node; if (cache_.size() > size_) { Node* tail = remove_tail(); cache_.erase(tail->key); delete tail; } } else { Node* node = iter->second; node->value = value; move_to_head(node); } } private: int size_; Node* head_; Node* tail_; std::unordered_map<int, Node*> cache_; void add_to_head(Node* node) { if (head_ == nullptr) { head_ = node; tail_ = node; } else { node->next = head_; head_->prev = node; head_ = node; } } void move_to_head(Node* node) { if (node == head_) { return; } else if (node == tail_) { tail_ = tail_->prev; tail_->next = nullptr; } else { node->prev->next = node->next; node->next->prev = node->prev; } node->next = head_; head_->prev = node; node->prev = nullptr; head_ = node; } Node* remove_tail() { Node* node = tail_; if (head_ == tail_) { head_ = nullptr; tail_ = nullptr; } else { tail_ = tail_->prev; tail_->next = nullptr; } return node; } }; ``` 在这个实现中,我们使用了一个双向链表保存缓存中的数据,并使用一个哈希表来提高查找效率。在 `get` 操作中,如果缓存中不存在目标数据则返回 -1,否则将目标数据移到链表头部并返回其值。在 `put` 操作中,如果缓存中不存在目标数据则创建一个新的节点并将其添加到链表头部,如果缓存已满则删除链表尾部的节点。如果缓存中已存在目标数据则将其值更新并将其移动到链表头部。 需要注意的是,在 `move_to_head` 和 `remove_tail` 操作中,我们需要判断目标节点是否已经在链表的头部或尾部,以避免对空指针进行操作。此外,在每次操作中,我们还需要更新哈希表中对应节点的指针。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值