zoukankan      html  css  js  c++  java
  • [leetcode] LRU Cache

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

    get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
    set(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.

    https://oj.leetcode.com/problems/lru-cache/

    什么是cache?

    Cache(高速缓存), 一个在计算机中几乎随时接触的概念。

    CPU中Cache能极大提高存取数据和指令的时间,让整个存储器(Cache+内存)既有Cache的高速度,又能有内存的大容量;

    操作系统中的内存page中使用的Cache能使得频繁读取的内存磁盘文件较少的被置换出内存,从而提高访问速度;

    数据库中数据查询也用到Cache来提高效率;

    即便是Powerbuilder的DataWindow数据处理也用到了Cache的类似设计。

    Cache的算法设计常见的有FIFO(first in first out)和LRU(least recently used)。根据题目的要求,显然是要设计一个LRU的Cache。

     

     

    梳理一下思路:对于Cache的每个数据块,我们设计一个数据结构来储存Cache块的内容,并实现一个双向链表,其中属性next和prev时双向链表的两个指针,key用于存储对象的键值,val用户存储要cache块对象本身。


    Cache的接口:

    查询:

      • 根据键值查询hashmap,若命中,则返回节点,否则返回null。

      • 从双向链表中删除命中的节点,将其重新插入到表头。

      • 所有操作的复杂度均为O(1)。

    插入:

      • 将新的节点关联到Hashmap

      • 如果Cache满了,删除双向链表的尾节点,同时删除Hashmap对应的记录

      • 将新的节点插入到双向链表中头部

    更新:

      • 和查询相似

    删除:

      • 从双向链表和Hashmap中同时删除对应的记录。

    思路:实现一个LRUCache的类,主要是get和set方法要保证高效。需要一个hashmap 和一个双向链表结合。hashmap用于在O(1)的时间内查找指定元素是否在cache里,而双向链表则允许在O(1)的时间移动元素到表头和删除尾部节点。

    第三遍更新:直接利用HashMap 和 LinkedList 结合。缺点:LinkedList 的remove方法是从头扫描的,不是O(1)的,所以还是建议自己实现双向链表

    import java.util.HashMap;
    import java.util.LinkedList;
    
    public class LRUCache {
    
        private static class Node {
            int key;
            int val;
    
            public Node(int k, int v) {
                key = k;
                val = v;
            }
    
            @Override
            public String toString() {
                return "Node [key=" + key + ", val=" + val + "]";
            }
    
        }
    
        int capacity;
        LinkedList<Node> list = new LinkedList<Node>();
        HashMap<Integer, Node> map = new HashMap<Integer, Node>();
    
        public LRUCache(int capacity) {
            this.capacity = capacity;
        }
    
        public int get(int key) {
            if (map.containsKey(key)) {
                Node cur = map.get(key);
                list.remove(cur);
                list.addFirst(cur);
                return cur.val;
            } else
                return -1;
    
        }
    
        public void set(int key, int val) {
            if (map.containsKey(key)) {
                Node cur = map.get(key);
                list.remove(cur);
                cur.val = val;
                list.addFirst(cur);
            } else {
                if (list.size() >= capacity) {
                    Node last = list.getLast();
                    map.remove(last.key);
                    list.removeLast();
                }
                Node newNode = new Node(key, val);
                list.addFirst(newNode);
                map.put(key, newNode);
            }
    
        }
    
        public static void main(String[] args) {
            LRUCache cache = new LRUCache(3);
            cache.set(1, 11);
            cache.set(2, 22);
            cache.set(3, 33);
            cache.set(4, 44);
            System.out.println(cache.get(1));
            System.out.println(cache.get(2));
            System.out.println(cache.get(3));
            System.out.println(cache.get(4));
    
        }
    
    }

    自己实现双向链表。

    import java.util.HashMap;
    
    public class LRUCache {
        private int capacity;
        private int len;
        private HashMap<Integer, DoubleLinkedListNode> map = new HashMap<Integer, DoubleLinkedListNode>();
        private DoubleLinkedListNode head;
        private DoubleLinkedListNode tail;
    
        public LRUCache(int capacity) {
            this.capacity = capacity;
            len = 0;
        }
    
        public int get(int key) {
            if (map.containsKey(key)) {
                DoubleLinkedListNode cur = map.get(key);
                removeNode(cur);
                setHead(cur);
                return cur.val;
            } else
                return -1;
        }
    
        public void set(int key, int value) {
            if (map.containsKey(key)) {
                DoubleLinkedListNode cur = map.get(key);
                cur.val = value;
                removeNode(cur);
                setHead(cur);
            } else {
                DoubleLinkedListNode newNode = new DoubleLinkedListNode(key, value);
                if (len < capacity) {
                    setHead(newNode);
                    map.put(key, newNode);
                    len++;
                } else {
                    map.remove(tail.key);
                    tail = tail.pre;
                    if (tail != null)
                        tail.next = null;
    
                    setHead(newNode);
                    map.put(key, newNode);
    
                }
    
            }
        }
    
        private void removeNode(DoubleLinkedListNode cur) {
            if (cur == null)
                return;
            DoubleLinkedListNode pre = cur.pre;
            DoubleLinkedListNode next = cur.next;
            if (pre != null) {
                pre.next = cur.next;
            } else
                head = next;
    
            if (next != null)
                next.pre = cur.pre;
            else
                tail = pre;
    
        }
    
        private void setHead(DoubleLinkedListNode cur) {
            cur.next = head;
            cur.pre = null;
            if (head != null)
                head.pre = cur;
    
            head = cur;
            if (tail == null)
                tail = cur;
        }
    
        public static void main(String[] args) {
            LRUCache cache = new LRUCache(1);
            cache.set(1, 11);
            cache.set(1, 111);
            System.out.println(cache.get(1));
            cache.set(2, 22);
            System.out.println(cache.get(1));
            System.out.println(cache.get(2));
        }
    
        private static class DoubleLinkedListNode {
            public int key;
            public int val;
            public DoubleLinkedListNode pre;
            public DoubleLinkedListNode next;
    
            public DoubleLinkedListNode(int key, int val) {
                this.key = key;
                this.val = val;
            }
        }
    
    }

    第四遍重写:

      要自己实现双向链表,因为LinkedList没有O(1)时间删除当前节点的方法,只能自己实现。

      head和tail都是dummy的,记得要初始化,有了dummy节点,删除和头上增加节点就简洁多了。

      注意capacity,len,head,tail几个变量运用。

      注意增加和删除时,map和链表同步操作。

    import java.util.HashMap;
    
    public class LRUCache {
        private int capacity;
        private HashMap<Integer, Node> map;
        private Node head = new Node(-1, -1);
        private Node tail = new Node(-1, -1);
        private int len;
    
        public LRUCache(int c) {
            capacity = c;
            map = new HashMap<Integer, Node>();
            len = 0;
            head.next = tail;
            tail.pre = head;
        }
    
        private static class Node {
            int key;
            int val;
            Node pre;
            Node next;
    
            public Node(int k, int v) {
                key = k;
                val = v;
            }
    
            public String toString() {
                return "[" + key + "," + val + "]";
            }
    
        }
    
        private void removeNode(Node cur) {
            Node pre = cur.pre;
            Node post = cur.next;
            pre.next = post;
            post.pre = pre;
            cur.pre = null;
            cur.next = null;
            len--;
        }
    
        private void addFirst(Node cur) {
            Node post = head.next;
            head.next = cur;
            post.pre = cur;
            cur.pre = head;
            cur.next = post;
            len++;
        }
    
        public int get(int key) {
            if (map.containsKey(key)) {
                Node cur = map.get(key);
                removeNode(cur);
                addFirst(cur);
                return cur.val;
            } else {
                return -1;
            }
    
        }
    
        public void set(int key, int val) {
            if (map.containsKey(key)) {
                Node cur = map.get(key);
                removeNode(cur);
                cur.val = val;
                addFirst(cur);
            } else {
                if (len >= capacity) {
                    Node toDel = tail.pre;
                    map.remove(toDel.key);
                    removeNode(toDel);
                }
    
                Node newNode = new Node(key, val);
                map.put(key, newNode);
                addFirst(newNode);
            }
    
        }
    
        public static void main(String[] args) {
            LRUCache cache = new LRUCache(1);
            cache.set(1, 11);
            cache.set(1, 111);
            System.out.println(cache.get(1));
            cache.set(2, 22);
            cache.set(3, 33);
            System.out.println(cache.get(2));
            System.out.println(cache.get(1));
            System.out.println(cache.get(3));
    
        }
    
    }

    参考:

    http://www.programcreek.com/2013/03/leetcode-lru-cache-java/

    http://blog.csdn.net/whuwangyi/article/details/15495845

  • 相关阅读:
    pycharm出现乱码
    Name-based virtual servers 给予名称的虚拟服务
    预建报为稳定版本
    nginx指令
    Module ngx_http_index_module nginx的首页模块
    我还在坚持中~
    手机端页面自适应解决方案—rem布局进阶版
    前端页面的适配使用rem换算
    js零碎知识汇总
    让input不可编辑
  • 原文地址:https://www.cnblogs.com/jdflyfly/p/3830446.html
Copyright © 2011-2022 走看看