手写LRU缓存

要求

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
get和put函数的时间复杂度因为O(1)

技术栈

HashMap和双向链表=LinkedHashMap的链式版本
HashMap:存放缓存数据<Integer, BiNode>
双向链表:存放节点顺序<BiNode>

举例

缓存容量:2
依次进行以下操作:
1. put(1,1)
2. put(2,2)
3. get(1)
3. put(3,3)
4. put(4,4)

图1 初始化虚拟头节点和尾节点

  1. put(1,1)

图2 插入1

  1. put(2,2)

图3 插入2

  1. get(1)

图4 获取key为1的值

由于要删除最近访问的值,因此将key为1的节点移到头节点
细节:先移除,再移动(这里要注意顺序,不然容易出问题,如果先移动到头节点,再移除会出错,如图5)。

图5 先移动后移除图

  1. put(3,3)

图6 put(3,3)

由于此时缓存元素为2,再插入一个元素,超过缓存容量,所以需要移除尾节点。然后将插入的元素移动到头部,如图6所示。
  1. put(4,4)

图7 put(4,4)

解释如4所示。

LRU缓存代码

class LRUCache {

    int size;                           // 元素个数
    int capacity;                       // 容量
    BiNode head;                        // 头节点
    BiNode tail;                        // 尾节点
    Map<Integer, BiNode> cache;         // 缓存
    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        this.cache = new HashMap<>();
        head = new BiNode();
        tail = new BiNode();
        head.next = tail;
        tail.pre = head;
    }

    public int get(int key) {
        BiNode node = cache.get(key);
        if (node == null) {
            return -1; // 不存在该元素
        } else {
            removeNode(node);   // 移除老元素
            addToHead(node);    // 放到头部
            return node.value;
        }
    }

    public void put(int key, int value) {
        BiNode node = cache.get(key);
        if(node == null){               // 不存在该元素
            BiNode newNode = new BiNode(key, value);
            addToHead(newNode);          // 放到头部
            cache.put(key, newNode);     // 放入缓存
            size++;
            if(size > capacity){
                int K = tail.pre.key;   // 移除的key
                cache.remove(K);        // 从缓存移除
                removeTailNode();       // 移除尾部节点
                size--;
            }
        }else {                         // 存在该元素
            BiNode newNode = new BiNode(key, value);
            addToHead(newNode);         // 放到头部
            removeNode(node);           // 移除老节点
            cache.put(key, newNode);    // 放入缓存
        }
    }

    private void removeNode(BiNode node){
        node.next.pre = node.pre;
        node.pre.next = node.next;
    }

    private void removeTailNode(){
        BiNode node = tail.pre;
        removeNode(node);
        node = null; // help GC
    }

    private void addToHead(BiNode node){
        node.next = head.next;
        node.next.pre = node;
        node.pre = head;
        head.next = node;
    }



    class BiNode{
        BiNode pre;     // 前向指针
        BiNode next;    // 后向指针
        int key;        // key
        int value;      // value
        public BiNode(){}
        public BiNode(int K, int V){
            key = K;
            value = V;
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值