Java版LRU缓存题解

今天看了一道比较具有实用价值的题目,即如何实现LRU缓存

题目描述如下:

image-20230805151512114

题目分析

这道题的话实际上是要设计一种数据结构,并且满足以下条件:

1、显然 cache 中的元素必须有时序,以区分最近使用的和久未使用的数据,当容量满了之后要删除最久未使用的那个元素腾位置。

2、我们要在 cache 中快速找某个 key 是否已存在并得到对应的 val;(联想到HashMap)

3、每次访问 cache 中的某个 key,需要将这个元素变为最近使用的,也就是说 cache 要支持在任意位置快速插入和删除元素。(联想到双向链表)

哈希表查找快,但是数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。所以结合一下,形成一种新的数据结构:哈希链表 LinkedHashMap

自己造轮子写LinkedHashMap

如果要自己实现LinkedHashMap,需要写一个双向链表和哈希表组合起来使用,如下面代码所示:

// 自己造轮子版实现LRU
class Node{
    public int key, val;
    public Node prev, next;
    public Node(int k, int v){
        this.key = k;
        this.val = v;
    }
}

class doubleList{
    // 头节点和尾节点
    private Node head, tail;
    // 链表长度
    private int size;

    // 初始化双向链表
    public doubleList(){
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.prev = head;
        size = 0;
    }

    // 向链表尾部添加节点
    public void addLast(Node x){
        x.prev = tail.prev;
        x.next = tail;
        tail.prev.next = x;
        tail.prev = x;
        size++;
    }

    // 删除链表中的x节点
    public void remove(Node x){
        x.prev.next = x.next;
        x.next.prev = x.prev;
        size--;
    }

    // 删除链表中第一个节点并返回该节点
    public Node removeFirst(){
        if(size==0) return null;
        Node first = head.next;
        remove(first);
        return first;
    }

    // 返回双链表的长度
    public int size(){
        return size;
    }
}

class LRUCache {

    // 定义一个HashMap,主要实现O(1)查找
    private HashMap<Integer, Node> map;
    // 定义一个双向链表来实现LRU算法
    private doubleList cache;
    // 最大容量
    private int cap;

    // 将某个key提升为最近使用过的
    private void makeRecently(int key){
        Node x = map.get(key);
        // 先在链表中删除这个节点
        cache.remove(x);
        // 然后在链表尾部添加这个节点
        cache.addLast(x);
    }

    // 添加最近使用的元素
    private void addRecently(int key, int value){
        Node x = new Node(key, value);
        // 加到链表尾部,表示最近使用过的节点
        cache.addLast(x);
        // 在map中添加映射
        map.put(key, x);
    }

    // 删除某一个 key 
    private void deleteKey(int key) {
        Node x = map.get(key);
        // 从链表中删除
        cache.remove(x);
        // 从 map 中删除
        map.remove(key);
    }

    // 删除最久未使用的元素 
    private void removeLeastRecently() {
        // 链表头部的第一个元素就是最久未使用的
        Node deletedNode = cache.removeFirst();
        // 同时从 map 中删除它的 key
        int deletedKey = deletedNode.key;
        map.remove(deletedKey);
    }

    public LRUCache(int capacity) {
        this.cap = capacity;
        map = new HashMap<>();
        cache = new doubleList();
    }
    
    public int get(int key) {
        if(!map.containsKey(key)){
            return -1;
        }
        makeRecently(key);
        return map.get(key).val;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            // 删除旧的数据
            deleteKey(key);
            // 新插入的数据为最近使用的数据
            addRecently(key, value);
            return;
        }
        
        if (cap == cache.size()) {
            // 删除最久未使用的元素
            removeLeastRecently();
        }
        // 添加为最近使用的元素
        addRecently(key, value);
    }
}

java提供的LinkedHashMap题解

class LRUCache {
    // 容量
    int cap;
    // 缓存,采用LinkedHashMap实现
    LinkedHashMap<Integer, Integer> cache;

    // 把一个key变成最近使用
    private void makeRecently(int key){
        int val = cache.get(key);
        // 先删掉在加到cache里面
        cache.remove(key);
        cache.put(key, val);
    }

    public LRUCache(int capacity) {
        this.cap = capacity;
        this.cache = new LinkedHashMap<>();
    }
    
    public int get(int key) {
        if(!cache.containsKey(key)){
            return -1;
        }
        makeRecently(key);
        return cache.get(key);
    }
    
    public void put(int key, int value) {
        if(cache.containsKey(key)){
            makeRecently(key);
            cache.put(key, value);
            return;
        }
        if(cache.size()>=cap){
            // 链表头部就是最久未使用的 key
            int lastKey = cache.keySet().iterator().next();
            cache.remove(lastKey);
        }
        // 将新的 key 添加链表尾部
        cache.put(key, value);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值