面试算法31:最近最少使用缓存

题目

请设计实现一个最近最少使用(Least Recently Used,LRU)缓存,要求如下两个操作的时间复杂度都是O(1)。

  • get(key):如果缓存中存在键key,则返回它对应的值;否则返回-1。
  • put(key,value):如果缓存中之前包含键key,则它的值设为value;否则添加键key及对应的值value。在添加一个键时,如果缓存容量已经满了,则在添加新键之前删除最近最少使用的键(缓存中最长时间没有被使用过的元素)。

分析

哈希表HashMap的get操作和put操作的时间复杂度都是O(1),但普通的哈希表无法找出最近最少使用的键,因此,需要在哈希表的基础上进行改进。

由于需要知道缓存中最近最少使用的元素,因此可以把存入的元素按照访问的先后顺序存入链表中。每次访问一个元素(无论是通过get操作还是通过put操作),该元素都被移到链表的尾部。这样,位于链表头部的元素就是最近最少使用的。

下面考虑如何实现把一个节点移到链表的尾部。这实际上包含两个步骤,首先要把节点从原来的位置删除,然后把它添加到链表的尾部。需要注意的是,在链表中删除一个节点,实际上是把它的前一个节点的next指针指向它的下一个节点。如果这个链表是单向链表,那么找到一个节点的前一个节点需要从链表的头节点开始顺序扫描每个节点,也就需要O(n)的时间。

为了快速找到一个节点的前一个节点从而实现用O(1)的时间删除一个节点,可以用双向链表来存储缓存中的元素。在双向链表中查找一个节点的前一个节点只需要顺着prev指针向前走一步,时间复杂度为O(1)。

因此,设计最近最少使用缓存需要结合哈希表和双向链表的特点。哈希表的键就是缓存的键,哈希表的值为双向链表中的节点,每个节点都是一组键与值的数对。

java 复制代码
public class Test {
    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(4);
        lruCache.put(1,1);
        lruCache.put(2,2);
        lruCache.put(3,3);
        lruCache.put(4,4);

        ListNode node1 = lruCache.head;
        while (node1 != null){
            System.out.println(node1.value);
            node1 = node1.next;
        }
        System.out.println("-------------------------");

        lruCache.get(2);
        ListNode node2 = lruCache.head;
        while (node2 != null){
            System.out.println(node2.value);
            node2 = node2.next;
        }
        System.out.println("-------------------------");

        lruCache.put(1,8);
        ListNode node3 = lruCache.head;
        while (node3 != null){
            System.out.println(node3.value);
            node3 = node3.next;
        }
        System.out.println("-------------------------");

        lruCache.put(5,5);
        ListNode node4 = lruCache.head;
        while (node4 != null){
            System.out.println(node4.value);
            node4 = node4.next;
        }
    }

    static class ListNode{
        public int key;
        public int value;
        public ListNode next;
        public ListNode prev;

        public ListNode(int k,int v){
            key = k;
            value = v;
        }
    }

    static class LRUCache{
        private ListNode head;
        private ListNode tail;
        private Map<Integer,ListNode> map;
        int capacity;

        public LRUCache(int cap){
            map = new HashMap<>();

            head = new ListNode(-1,-1);
            tail = new ListNode(-1,-1);
            head.next = tail;
            tail.prev = head;

            capacity = cap;
        }

        public int get(int key){
            ListNode node = map.get(key);
            if (node == null){
                return -1;
            }

            moveToTail(node,node.value);

            return node.value;
        }

        public void put(int key,int value){
            if (map.containsKey(key)){
                moveToTail(map.get(key),value);
            }else {
                if (map.size() == capacity){
                    ListNode toBeDeleted = head.next;
                    deleteNode(toBeDeleted);

                    map.remove(toBeDeleted.key);
                }

                ListNode node = new ListNode(key,value);
                intsertToTail(node);

                map.put(key,node);
            }
        }

        private void moveToTail(ListNode node,int newValue){
            deleteNode(node);

            node.value = newValue;
            intsertToTail(node);
        }

        private void deleteNode(ListNode node){
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }

        private void intsertToTail(ListNode node){
            tail.prev.next = node;
            node.prev = tail.prev;
            node.next = tail;
            tail.prev = node;
        }
    }
}
相关推荐
Bro_cat18 小时前
MySQL面试 八股文20道
数据库·mysql·面试
希望有朝一日能如愿以偿18 小时前
力扣每日一题:使数组和能被p整除
数据结构·算法·leetcode
Christo318 小时前
AAAI-2013《Spectral Rotation versus K-Means in Spectral Clustering》
人工智能·算法·机器学习·数据挖掘·kmeans
葵花楹18 小时前
【补题】【atcoderabc434】【codeforces1067】
算法
roman_日积跬步-终至千里18 小时前
【模式识别与机器学习】AdaBoost算法:集成学习的基本原理与AdaBoost算法的应用
算法·机器学习·集成学习
mit6.82418 小时前
中位数贪心|前缀和_距离和ret=l+r_1
算法
一匹电信狗18 小时前
【LeetCode】栈和队列进阶题目
c++·算法·leetcode·职场和发展·stl·栈和队列
机器学习之心18 小时前
198种组合算法+优化TCN时间卷积神经网络+SHAP分析+新数据预测+多输出!深度学习可解释分析,强烈安利,粉丝必备!
深度学习·算法·shap分析·tcn时间卷积神经网络
代码游侠18 小时前
数据结构——线性表
linux·c语言·数据结构·学习·算法
c***871918 小时前
Nginx 缓存清理
运维·nginx·缓存