面试题:如何实现一个简单的LRU缓存设计 Java解法

本文介绍了如何使用Java设计一个LRU缓存结构,详细解释了基于LinkHashMap的实现原理,包括set和get操作的时间复杂度要求以及当缓存满时如何移除最不常用记录。解题思路涉及将双向链表与哈希表结合,创建一个包含虚拟头尾节点的高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LRU缓存设计题(即Java中LinkHashMap实现原理)

题表

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

[要求]

  1. set和get方法的时间复杂度为O(1)
  2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
  3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

示例1

输入

[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3

返回值

[1,-1]

说明

第一次操作后:最常使用的记录为("1", 1)
第二次操作后:最常使用的记录为("2", 2),("1", 1)变为最不常用的
第三次操作后:最常使用的记录为("3", 2),("1", 1)还是最不常用的
第四次操作后:最常用的记录为("1", 1),("2", 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录("2", 2),加入记录("4", 4),并且为最常使用的记录,然后("3", 2)变为最不常使用的记录

解题思路

实际上就是实现一个有序的HashMap,具体的实现方式就是把双向链表与HashMap结合起来,创建一个全新的LinkMap类。

改造的LinkMap中的hashMap存储的value为链表的节点类型,这样方便在增加节点和查询节点时,更改双向链表中节点的位置。

合理的利用dummyHeaddummyTail虚拟头尾节点,可以忽略链表的边际情况,对移动操作进行统一处理。

通过sizecapacity记录LinkMap中可以缓存的最大数量,在超出阈值后删除使用最不频繁的数据。

具体实现代码和解题代码如下:

public class Solution {
    /**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LRU (int[][] operators, int k) {
        // write code here
        LinkMap linkMap = new LinkMap(k);
        List<Integer> list = new ArrayList<>();
        for (int[] op : operators) {
            if (op[0] == 1) {
                linkMap.set(op[1], op[2]);
            } else {
                list.add(linkMap.get(op[1]));
            }
        }
        return list.stream().mapToInt(Integer::intValue).toArray();
    }

}
class LinkMap{
    static class LinkNode{
        int key;
        int value;
        LinkNode pre;
        LinkNode next;

        public LinkNode() {

        }

        public LinkNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    HashMap<Integer, LinkNode> map;
    LinkNode dummyHead;
    LinkNode dummyTail;
    int capacity;
    int size = 0;

    public LinkMap(int K) {
        capacity = K;
        map = new HashMap<>();
        dummyHead = new LinkNode();
        dummyTail = new LinkNode();
        dummyHead.next = dummyTail;
        dummyTail.pre = dummyHead;
    }

    public void set(int key, int value) {
        LinkNode node = new LinkNode(key, value);
        map.put(key, node);
        moveToHead(node);
        size++;
        if (size > capacity) {
            removeTail();
        }
        
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        LinkNode node = map.get(key);
        moveToHead(node);
        return node.value;
    }

    public void moveToHead(LinkNode node) {
        LinkNode head = dummyHead.next;
        if (node.pre != null) {
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }
        node.next = head;
        head.pre = node;
        dummyHead.next = node;
    }

    public void removeTail() {
        map.remove(dummyTail.pre.key);
        dummyTail.pre.pre.next = dummyTail;
        dummyTail.pre = dummyTail.pre.pre;
        size--;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值