lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1. 题目

 

https://leetcode.cn/problems/lru-cache/

 

2. 解法

解题思路是

使用一个双向链表和一个哈希表来实现一个LRU缓存机制。

双向链表用来存储最常使用的键值对,最近使用的元素放在链表的表头,链表中最后一个元素是使用频率最低的元素。

哈希表用来记录对应的<key,<key, value>>,用于查找现在的缓存中是否有key及其value,以及快速定位链表中的节点。

当需要获取数据时,先在哈希表中查找是否有对应的key,如果有,则返回对应的value,并将该节点移动到链表的表头。如果没有,则返回-1。

当需要写入数据时,先在哈希表中查找是否有对应的key,如果有,则更新对应的value,并将该节点移动到链表的表头。如果没有,则在链表的表头插入新的节点,并在哈希表中添加新的映射。如果此时缓存已满,则删除链表的最后一个节点,并在哈希表中删除对应的映射。

 

这道题的解题思路可以分为以下几个步骤:

  1. 创建一个双向链表和一个哈希表。
  2. 定义双向链表的节点类,包含key,value,pre和next四个属性。
  3. 定义双向链表的数据结构,包含head,tail,size三个属性,以及addFirst,remove,removeLast和size四个方法。
  4. 定义LRU缓存的数据结构,包含map,cache和cap三个属性,以及get和put两个方法。
  5. 在get方法中,先在map中查找key是否存在,如果存在,则返回value,并将节点移动到链表头部;如果不存在,则返回-1。
  6. 在put方法中,先在map中查找key是否存在,如果存在,则更新value,并将节点移动到链表头部;如果不存在,则在链表头部添加新节点,并在map中添加映射;如果缓存已满,则删除链表尾部的节点,并在map中删除映射。

 

class LRUCache {
    //定义双向链表的节点类
    class Node{
        int key;
        int value;
        Node pre;
        Node next;
        public Node(int key,int value){
            this.key=key;
            this.value=value;
        }
    }
    //定义双向链表的数据结构
    class DoubleList{
        //头尾虚节点
        Node head,tail;
        //链表元素数
        int size;
        public DoubleList(){
            //初始化双向链表的数据
            head=new Node(0,0);
            tail=new Node(0,0);
            head.next=tail;
            tail.pre=head;
            size=0;
        }
        //在链表头部添加节点x,时间O(1)
        public void addFirst(Node x){
            x.next=head.next;
            x.pre=head;
            head.next.pre=x;
            head.next=x;
            size++;
        }
        //删除链表中的x节点(x一定存在)
        //由于是双链表且给的是目标Node节点,时间O(1)
        public void remove(Node x){
            x.pre.next=x.next;
            x.next.pre=x.pre;
            size--;
        }
        //删除链表中最后一个节点,并返回该节点,时间O(1)
        public Node removeLast(){
            if(tail.pre==head){
                return null;
            }
            Node last=tail.pre;
            remove(last);
            return last;
        }
        //返回链表长度,时间O(1)
        public int size(){return size;}
    }

    //key 映射到 Node(key,val)
    private HashMap<Integer,Node> map;
    //Node(k1,k2) 双向链表:k1 - k2 - ...
    private DoubleList cache;
    //最大容量
    private int cap;

    public LRUCache(int capacity) {
        map=new HashMap<>();
        cache=new DoubleList();
        this.cap=capacity;
    }

    /* 将某个key提升为最近使用的 */
    private void makeRecently(int key){
        Node x=map.get(key);
        //先从链表中删除这个节点
        cache.remove(x);
        //重新插到队头
        cache.addFirst(x);
    }

    /* 添加最近使用的元素 */
    private void addRecently(int key,int val){
        Node x=new Node(key,val);
        //链表头部就是最近使用的元素
        cache.addFirst(x);
        //别忘了在map中添加key的映射
        map.put(key,x);
    }

    /* 删除某一个key */
    private void deleteKey(int key){
        Node x=map.get(key);
        //从链表中删除
        cache.remove(x);
        //从map中删除
        map.remove(key);
    }

    /* 删除最久未使用的元素 */
    private void removeLeastRecently(){
       // 链表尾部的元素就是最久未使用的元素
       Node deleteNode=cache.removeLast();
       //别忘了从map中删除它的key
       int deletedKey=deleteNode.key;
       map.remove(deletedKey);
    }

    public int get(int key) {
       if(!map.containsKey(key)){
           return -1;
       }
       //将该数据提升为最近使用的
       makeRecently(key);
       return map.get(key).value;

    }

    public void put(int key, int value) {
      if(map.containsKey(key)){
          //删除旧的数据
          deleteKey(key);
          //新插入的数据为最近使用的数据
          addRecently(key,value);
          return ;
      }
      if(cap==cache.size()){
          //删除最久未使用的元素
          removeLeastRecently();
      }
      //添加为最近使用的元素
      addRecently(key,value);

    }
}

  

3. 总结

posted on 2023-04-18 16:36  白露~  阅读(11)  评论(0编辑  收藏  举报