Loading

【力扣】146. LRU缓存机制

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

 

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

 

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得关键字 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得关键字 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache

class LRUCache {

//LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。
    private static class Node{
        private int key;

        private int value;

        private Node prev; //前置结点

        private Node next; //下一个节点

        public Node(){}

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

    }

    //当容量满的时候,采用LRU淘汰策略
    //1.淘汰最少使用的数据
    //2.淘汰最早放进来的数据

    private Map<Integer,Node> map = new HashMap<Integer,Node>();

    //标识容量 是一个固定值
    private int capacity;

    //标识大小,当前大小
    private int size;

    //头结点
    private Node head;

    //尾节点
    private Node tail;


    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.size = 0;
        // 使用伪头部和伪尾部节点
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.prev = head;
    }
    
    //如果 key 不存在,则返回 -1−1;
    //如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值
    public int get(int key) {
        Node  result = map.get(key);
        if(result == null){
            return -1;
        }
        //通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部
        result.prev.next = result.next;
        result.next.prev = result.prev;
            //把当前节点放到头结点
            result.prev = head;
            result.next = head.next;
            head.next.prev = result;
            head.next = result;
        return result.value;
    }
    
    /**
    对于 put 操作,首先判断 key 是否存在:
    如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
    如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
    **/
    public void put(int key, int value) {
        Node  result = map.get(key);
        //若是没有则放入新的node节点
        if(result == null){
            Node node = new Node(key,value);
            //添加到hash表
            map.put(key,node);
            //把新加入的节点放到链表的头部
            node.prev = head;
            node.next = head.next;
            head.next.prev = node;
            head.next = node;
            ++size;
            //如果当前大小已经超出了容量,就把最久未使用的删除
            if(size > capacity){
                //去除hash表中的值
                map.remove(tail.prev.key);
                //去除链表中最久未使用的节点
                tail.prev.prev.next = tail;
                tail.prev = tail.prev.prev;
                --size;
            }
        } else {
            //若是有值,需要把原来的值覆盖
            result.value = value;
            map.put(key,result);

            //把链表中的节点放到头结点
            //先把当前节点删除
            result.prev.next = result.next;
            result.next.prev = result.prev;

            //把当前节点放到头结点
            result.prev = head;
            result.next = head.next;
            head.next.prev = result;
            head.next = result;
        }
    }
}

/**
 * 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);
 */

 

posted @ 2020-10-28 14:00  冯廷鑫  阅读(211)  评论(0编辑  收藏  举报