LRU缓存机制的两种实现:LInkedHashMap实现、自己构建双链表+HashMap实现

问题描述:运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

解法一:LinkedHashMap实现
public class LRUCache {

    private Integer capacity;

    LinkedHashMap<Integer,Integer> cache  = new LinkedHashMap<>();

    public LRUCache(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        if(!cache.containsKey(key)){
            return -1;
        }
        makeKeyRecentUse(key);
        return cache.get(key);
    }

    public void put(int key, int value) {
    //如果缓存已存在,因为key的不可阿重复性,直接调用hashmap的put方法覆盖之前的key-value,覆盖之后呢就返回
        if(cache.containsKey(key)){
            cache.put(key,value);
            makeKeyRecentUse(key);
            return;
        }
    //如果容量已大于设置的初始容量,因为链表头的元素为最久未使用,所以先删除表头的元素,在讲新的key-value加载表尾
        if(cache.size() >= this.capacity){
            Iterator<Integer> iterator = cache.keySet().iterator();
            cache.remove(iterator.next());
        }
        cache.put(key,value);

    }

   //根据key值将该key-value移到表尾,表示最近使用
    private void makeKeyRecentUse(int key){
        Integer value = cache.get(key);
        cache.remove(key);
        cache.put(key,value);
    }

}

第二种解法:自定义双链表和hashmap解决
public class LRUCache {

    private Integer capacity;
    //自定义缓存来存储每一个节点
    SelfList cache = new SelfList();

    //map里面的key就是key,但value是每一个节点
    HashMap<Integer,Node> map = new HashMap<>();
    public LRUCache(Integer capacity) {
        this.capacity = capacity;
    }

    public int get(int key){
    //如果map已经包含该key-node,就将双链表里的该节点移动到链表尾部,表示最近使用,不包含就返回-1
        if(!map.containsKey(key)){
            return -1;
        }
        //将双链表中的该节点移到链表尾部
        makeKeyRecentUse(key);
        Node node = map.get(key);
        return node.value;
    }

    public void put(int key,int value){

       //如果该map已经含有该key,那就覆盖
       //先从map中定位到旧节点,然后删除map的该key-node和双链表中旧node,再将新key-node和心node分别加到map和双链表中
       //记得return 不然会添加两次
       if(map.containsKey(key)){
            Node node = map.get(key);
            cache.remove(node);
            map.remove(key);
            Node newNode = new Node(key, value);
            cache.addLast(newNode);
            map.put(key,newNode);
            return;
        }

       //如果缓存的个数已经大于预设的容量,那么就删除双链表的表头节点(不是head节点嗷),并且删除map中的旧key-node
        //并且将新的额key-node和心node分别加到map和双链表中,记得返回
        if(cache.size() >= capacity){
            Node node = cache.removeFirst();
            map.remove(node.key);
            Node newNod1 = new Node(key,value);
            map.put(key,newNod1);
            cache.addLast(newNod1);
            return;
        }

        //正常添加
        Node newNode2 = new Node(key, value);
        map.put(key,newNode2);
        cache.addLast(newNode2);
    }

   //将已有并且不需要覆盖的节点移动到双链表尾部,这里map不用变,不涉及到数值的变化
    private void makeKeyRecentUse(int key){
        Node node = map.get(key);
        cache.remove(node);
        cache.addLast(node);
    }


}

class Node{
     int key;
     int value;

    Node prev ;
    Node next ;

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

class SelfList{
    Node head,tail;
    int size;

   //双链表构建 初始化
    public SelfList() {
        head  = new Node(0,0);
        tail = new Node(0,0);
        head.next = tail;
        tail.prev = head;
        size = 0;
    }


    public int size(){
        return size;
    }

   //在链表尾部添加元素
    public void addLast(Node node){
        tail.prev.next = node;
        node.next = tail;
        node.prev = tail.prev;
        tail.prev = node;
        size++;
    }
   //移除头部第一个节点
    public Node removeFirst(){
        if(head.next == null){
            return null;
        }
        Node first = head.next;
        first.next.prev = head;
        head.next = first.next;
        size--;

        return first;
    }
    //移除某一个节点
    public void remove(Node node){
        node.prev.next = node.next;
        node.next.prev = node.prev;
        size--;
    }


}

上一篇:【C/C++】【面经】2022 阿里巴巴 面经;(C++ 方向/CTO线)(更新:一面;)


下一篇:SQL按照日、周、月、年统计数据