leetcode 146. LRU 缓存机制

@(labuladong的算法小抄)[哈希链表]

leetcode 146. LRU 缓存机制

题目描述

在这里插入图片描述

解题思路

参考:labuladong的算法小抄P218

Java内置类型LinkedHashMap

面试官一般会期望读者能够自己实现一个简单的双向链表,而不是使用语言自带的、封装好的数据结构。在 Java 语言中,有数据结构 LinkedHashMap。这种做法不会符合面试官的要求,因此下面只给出使用封装好的数据结构实现的代码,而不多做阐述。

class LRUCache {
    //cache的容量
    private int cap;
    //LinkedHashMap的put是插入到尾部,因此,cache的尾部代表最近使用,头部代表最久未使用
    private LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();
    public LRUCache(int capacity) {
        this.cap = capacity;
    }
    public int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        //将key变为最近使用
        makeRecently(key);
        return cache.get(key);
    }

    public void put(int key, int value) {
	    /* 插入判满 */
	    if (this.cap <= 0) return;
        //如果cache中已有key,则直接修改,并移到最近使用的队尾
        if (cache.containsKey(key)) {
            cache.put(key, value);
            makeRecently(key);
            return;
        }
        //如果当前cache容量已满,则要先删除链表头部的最久未使用的key
        if (cache.size() >= this.cap) {
            int oldestKey = cache.keySet().iterator().next();
            cache.remove(oldestKey);
        }
        //将key插入到尾部
        cache.put(key, value);
    }
    //将key变为最近使用
    private void makeRecently(int key) {
        int val = cache.get(key);
        //删除key,重新插入队尾
        cache.remove(key);
        cache.put(key, val);
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

双向链表+哈希表

用自顶向下的思考方法

首先,列出getput函数的需求:
在这里插入图片描述

然后,根据getput函数的需求,明确出四个函数,将链表cache和哈希表map的操作封装起来,尽量让LRU的主方法getput避免直接操作cachemap的细节。由于map有自带的函数可以移除和添加key,所以只需要接下来只需关注链表的相关操作:
在这里插入图片描述

最后,根据上面四个函数需要对链表进行的操作,在双向链表的类中定义出三个API函数以供调用:
在这里插入图片描述


class LRUCache {
    //定义双链表的节点类型
    class Node {
        public int key, val;
        public Node next, prev;

        public Node(int k, int v) {
            this.key = k;
            this.val = v;
        }
    }

    //依靠Node类型构建双链表,并实现LRU算法必需的几个API
    class DoubleList {
        //头尾虚节点
        private Node head, tail;
        //链表元素数
        private int size;

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

        //在链表尾部添加节点x,时间复杂度为o(1)
        public void addLast(Node x) {
            x.prev = tail.prev;
            x.next = tail;
            tail.prev.next = x;
            tail.prev = x;
            size++;
        }

        //删除链表中的x节点(x一定存在),时间复杂度为o(1)
        public void remove(Node x) {
            x.prev.next = x.next;
            x.next.prev = x.prev;
            size--;
        }

        //删除链表中第一个节点,并返回该节点,时间复杂度为o(1)
        public Node removeFirst() {
            if (head.next == tail) {
                return null;
            }
            Node first = head.next;
            remove(first);
            return first;
        }

        //返回链表的长度,时间复杂度为o(1)
        public int size() {
            return size;
        }
    }

    //hashmap和doublelist组合实现java内置的LinkedHashMap功能
    private HashMap<Integer, Node> map;
    private DoubleList cache;
    //最大容量
    private int cap;

    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 (this.cap <= 0) return;
        if (map.containsKey(key)) {
            //删除旧的数据
            removeKey(key);
            //新插入的数据为最近使用的数据
            addRecently(key, value);
            return;
        }
        //如果容量不够了,就要先删除最久未使用的元素
        if (cache.size() >= cap) {
            removeLeastRecently();
        }
        addRecently(key, value);
    }

    /* 由于map和cache是组合实现的,因此用以下四个函数进行封装,将map和cache的操作绑定起来 */
    //将某个key提升为最近使用的
    private void makeRecently(int key) {
        Node x = map.get(key);
        //先从链表中删除这个节点
        cache.remove(x);
        //重新插到队尾
        cache.addLast(x);
    }

    //添加最近使用的元素
    private void addRecently(int key, int val) {
        Node x = new Node(key, val);
        cache.addLast(x);
        //别忘了在map中添加key的映射
        map.put(key, x);
    }

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

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

}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值