146. LRU 缓存机制

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

  • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
  • int get(int key) Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

Follow up:
Could you do get and put in O(1) time complexity?

Example 1:

Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1);    // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2);    // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1);    // return -1 (not found)
lRUCache.get(3);    // return 3
lRUCache.get(4);    // return 4

Constraints:

  • 1 <= capacity <= 3000
  • 0 <= key <= 3000
  • 0 <= value <= 104
  • At most 3 * 104 calls will be made to get and put.

LRU缓存器。

实现这个缓存器,时间复杂度要求是O(1)。

LRU缓存器是在一定的容量范围内存了一些node,按照上一次被访问的时间由新到旧排列,越近被访问的node应该排得越靠前。有几个函数需要实现。

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

 

时间O(1) - required

空间O(n) - hashmap + DLL

思路:

双向链表(DLL) + hashmap。

因为题意要求了时间复杂度必须是O(1)所以只有hashmap才能满足这个时间复杂度。

至于为什么是DLL而不是单链表,则是为了node之间的移动方便,也是为了添加删除节点的时候能更加高效。

 

 

class LRUCache {
        /**
         * map<K,Node>
         */
        private HashMap<Integer,Node> keyNodeMap;
        /**
         * 自己组织的双链表
         */
        private NodeDoubleLinkedList nodeList;
        /**
         * 缓存容量
         */
        private Integer capacity;

        /***
         * 构造函数初始化结构
         */
        public LRUCache(Integer capacity) {
            if (capacity < 1) {
                throw new RuntimeException("capacity should be more than 0");
            }
            keyNodeMap = new HashMap<>();
            nodeList = new NodeDoubleLinkedList();
            this.capacity = capacity;
        }

        /**
         * get(k)
         */
         public int get(int k) {
            //是否在Map中
            if (keyNodeMap.containsKey(k)) {
                //通过K 定位到Node
                Node res = keyNodeMap.get(k);
                //刷新nodeList
                this.nodeList.moveNodeToTail(res);
                return res.v;
            }
            return -1;
        }

        /**
         * set(k,v)
         */
         public void put(int k, int v) {
            //如已存在于表中,则刷新节点,并更新node.v
            if (keyNodeMap.containsKey(k)) {
                Node res = keyNodeMap.get(k);
                res.v = v;
                this.nodeList.moveNodeToTail(res);
            } else {
                //不存在则加入
                Node node = new Node(k, v);
                //放入表中
                keyNodeMap.put(k, node);
                //加入到链表上
                this.nodeList.addNode(node);
                //如果容量超限,则移除头节点,交从表中删除
                if (keyNodeMap.size() == capacity + 1) {
                    Node removeNode = this.nodeList.removeHead();
                    keyNodeMap.remove(removeNode.k);
                }
            }
        }

          public class Node {
        public int k;
        public int v;
        private Node pre;
        private Node next;

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

    /**
     * 先准备双链表的api
     */
    public class NodeDoubleLinkedList {
        private Node head;
        private Node tail;

        public NodeDoubleLinkedList() {
            this.head = null;
            this.tail = null;
        }

        /**
         * 添加节点的操作,添加到结尾
         */
        public void addNode(Node node) {
            //如等待加的节点为空,直接返回
            if (node == null) {
                return;
            }
            //如果head为null,则挂在头节点
            if (head == null) {
                head = node;
                tail = node;
            }
            //否则挂在结尾的节点
            tail.next=node;
            node.pre=tail;
            node.next=null;
            tail=node;
        }

        /**
         * 先保证这个节点存在的情况下考虑此问题
         * 如果移动节点到尾部:先断联,再加到结尾节点
         */
        public void moveNodeToTail(Node node) {
            //如果要移动的是尾节点,则不用操作
            if (this.tail == node) {
                return;
            }
            //先断联
            //如果要移动的是头节点,更改头节点为老头的下一个节点
            if(head==node){
               head=head.next;
               head.pre=null;
            }else{//如果不是头尾节点,则是中间节点,先断联
                node.pre.next=node.next;
                node.next.pre=node.pre;
            }

            //再移到尾节点
            tail.next=node;
            node.pre=tail;
            node.next=null;
            tail=node;

        }

        /** 
         * 移除节点
        */
        public Node removeHead() {
            //你要操作头节点,则必须先检查head的状态
            //如果head null
            if (head == null) {
                return null;
            }
            //记录一下老头
            Node res = head;
            //如果头节点是head==tail
            if (head == tail) {
                head = null;
                tail = null;
            } else {//链表节点个数>1
                head = res.next;
                head.pre = null;
                res.next = null;//help gc
            }
            return res;
        }
    }
 
}

  


posted @ 2021-08-30 10:50  sherry001  阅读(71)  评论(0编辑  收藏  举报