LeetCode 之 LRU Cache解决思路

来源:互联网 发布:频谱仪软件 编辑:程序博客网 时间:2024/04/30 00:21

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

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.


这道题主要是实现“最近最少使用替换”算法。

Cache的大小是有限的,当增加新的值时,如果没有额外空间,就需要采用替换算法。替换算法的核心就是要维护一个状态,即:哪个值应该被替换出去,哪个值是最新的。

同时当调用get方法时,需要更新刚索引过的值,将其状态设置为最新,再更新其他值的状态,最近最少使用的那个值将更新为:下次被替换出去。set方法同样需要更新各个值的状态。


根据上述的分析,我们可以采用单向链表来实现该替换算法。该链表需要维护一个尾指针。我们将最新状态的值放置在尾部,最近最少使用的值放在头部。当没有额外空间时,加入新值,我们在尾部添加,同时删除头部的旧值。当更新值得状态时,对应的操作就是链表的删除和插入操作。


但是链表的缺点就是遍历耗时。如何在O(1)时间内访问到链表中的值呢?可以结合Hash表的性质。hash表内存储节点的key值和节点的指针。当调用get方法时,利用hashMap获取到包含该key的节点,时间为O(1)。此时,需要做的就是要维护hashmap和链表的一致性。


import java.util.HashMap;public class LRUCache {    private LinkList list;        public LRUCache(int capacity) {        list = new LinkList(capacity);    }        public int get(int key) {        return list.get(key);    }        public void set(int key, int value) {        list.set(key, value);    }        public void print(){    list.printList();    }class LinkList{    private int capacity;//链表总长度    private int size;//当前链表节点个数    private Node head;//头指针    private Node tail;//尾指针    private HashMap<Integer, Node> map;        public LinkList(int capacity){        this.capacity = capacity;        this.size = 0;        head = new Node();        tail = head;        tail.next = null;        map = new HashMap<Integer,Node>(capacity);    }        public int get(int key){    int val = -1;        if(map.containsKey(key)){        Node node = map.get(key);        val = node.value;        this.update(node);//更新链表和hashmap状态        return val;        }else{        return val;        }    }    public void set(int key, int value){    if(map.containsKey(key)){    Node node = map.get(key);    node.value = value;    this.update(node);    }else{    Node node = new Node(key, value);node.next = null;tail.next = node;tail = node;map.put(key, node);    if(size < capacity){    size++;       }else{    Node p = head.next;    int k = p.key;    head.next = p.next;    p.next = null;    p = null;    map.remove(k);    }    }         }     //当调用get和set方法时,当前节点应该需要置于最新状态,即将该节点放置到链表的尾部。采用的是单向链表,节点的删除需要从头遍历链表到当前节点的前一个节点,时间//时间复杂度为O(n),这里采用O(1)的时间复杂度删除节点p。//交换节点p和p.next的值,此时删除p.next,即将该节点插入到尾部即可。但需要注意的是在hashmap中就要将p.key和p.next.key的节点交换一下,否则hashmap维护的节点//就是错误的了。 public void update(Node p){    if(p != tail){        Node p2 = p.next;        map.put(p.key, p2);//更新hashmap中的节点        map.put(p2.key, p);//更新hashmap中的节点            int temp = p.key;            p.key = p2.key;            p2.key = temp;            temp = p.value;            p.value = p2.value;            p2.value = temp;                   if(p2 != tail){                p.next = p2.next;                p2.next = null;                tail.next = p2;                tail = p2;                         }    }    }        public void printList(){    Node p = head.next;    while(p != null){    System.out.print(p.key +"->" + p.value + ",");    p = p.next;    }    System.out.println();    }}class Node{    int key;    int value;    Node next;        public Node(){    }        public Node(int key, int value){        this.key = key;        this.value = value;    }}



0 0