Leetcode146 LRU缓存机制 Map+双端链表版本

在这里插入图片描述
在这里插入图片描述

思想

LRU的思想就是当缓存满了的时候,把最近最久没有使用过的数据从缓存中删除掉。因此需要一个双端队列来维护这种使用次序。当一个key-value新加入缓存,或者进行更改时,需要把该key-value移动到队列的首部(因为这个键值对是最近使用的)。

双端队列的数据结构如下:

class DLinkedNode {
        int key;
        int value;
        DLinkedNode pre;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
    }

可见一个双端队列的节点,相当于缓存中的一个键值对key-value。因为要维护LRU的顺序,所以该键值对维护着pre和next前后两个指针。

几种情况

设计LRU缓存,最重要的无非就是get和put函数,其中get非常简单,而put的情况大概可以分为如下两种:

(1)要put的key不存在于缓存中;

(2)要put的key存在于缓存中。

对于(1)来说,就是添加一个key-value的Node到双端链表的头部即可。但是要注意,缓存要维护一个size,如果在添加该键值对以后,出现了size>capacity的情况,则需要将最久未使用(双端链表队尾)的元素干掉。

对于(2)来说,此时需要做的就是更新缓存中对应key的value值,并且把该节点移动到双端链表的头部,作为最新使用过的节点。

值得注意的是:这里我们要判断一个键值对key-value是否存在于缓存中,所以需要用到HashMap这个数据结构(HashMap<Integer, DLinkedNode>,key就是标着这某一键值对的key,DLinkedNode表示该key所对应的键值对的key-value和顺序信息)。

所以根据上面信息,put函数大概就可以写出来了:

public void put(int key, int value) {
        DLinkedNode node = mp.get(key);	//mp就是HashMap
        if(node == null) {
            DLinkedNode node1 = new DLinkedNode(key, value);
            mp.put(key, node1);
            addToHead(node1);
            size++;
            if(size>capacity) {
                DLinkedNode tail = removeTail();
                mp.remove(tail.key);
                size--;
            }
        }
        else {
            node.value = value;
            moveToHead(node);
        }
    }

同样的,get函数很简单:就是从HashMap中看看有没有为key的键值对,如果有则返回该键值对的value,并把它移动到双端链表的头部;否则则返回-1。其代码如下所示:

    public int get(int key) {
        DLinkedNode node = mp.get(key);
        if(node == null) {
            return -1;
        }
        else {
            moveToHead(node);
            return node.value;
        }
    }

双端链表的操作

根据上面我们可以发现,双端队列需要四种操作:

(1)在头部插入节点(对应put一个不存在的键值对):

    public void addToHead(DLinkedNode node) {
        node.pre = head;
        node.next = head.next;
        head.next.pre = node;
        head.next = node;
    }

(2)在尾部删除节点(对应一个键值对过期):

    public DLinkedNode removeTail()
        DLinkedNode res = tail.pre;
        removeNode(res);
        return res;
    }

(3)删除中间某个点(对应更新一个键值对,要把它插入到头部,还需要在链表中删除点):

    public void removeNode(DLinkedNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

(4)将节点移动到头部,对应(3)的情况,相当于把(1)和(3)结合起来:

    public void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

AC代码

class LRUCache {

    class DLinkedNode {
        int key;
        int value;
        DLinkedNode pre;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
    }
    private Map<Integer, DLinkedNode> mp = new HashMap();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.pre = head;
    }
    
    public int get(int key) {
        DLinkedNode node = mp.get(key);
        if(node == null) {
            return -1;
        }
        else {
            moveToHead(node);
            return node.value;
        }
    }
    
    public void put(int key, int value) {
        DLinkedNode node = mp.get(key);
        if(node == null) {
            DLinkedNode node1 = new DLinkedNode(key, value);
            mp.put(key, node1);
            addToHead(node1);
            size++;
            if(size>capacity) {
                DLinkedNode tail = removeTail();
                mp.remove(tail.key);
                size--;
            }
        }
        else {
            node.value = value;
            moveToHead(node);
        }
    }

    public void addToHead(DLinkedNode node) {
        node.pre = head;
        node.next = head.next;
        head.next.pre = node;
        head.next = node;
    }

    public void removeNode(DLinkedNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

    public void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    public DLinkedNode removeTail() {
        DLinkedNode res = tail.pre;
        removeNode(res);
        return res;
    }
}

/**
 * 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);
 */
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值