【LeetCode算法学习笔记】LRU算法

labuladong公众号算法学习笔记

LRU算法

LRU算法是一种缓存淘汰策略。
LRU全称为Least Recently Used,也就是我们认为最近使用过的数据应该是有用的。

算法描述

//缓存容量为2
LRUCache cache = new LRUCache(2);
//可以理解为队列,左为队头,右为队尾。
//最近使用的在队头,久未使用的在队尾。
//圆括号表示键值对(key,val)
cache.put(1,1);//cache=[(1,1)]
cache.put(2,2);//cache=[(2,2),(1,1)]
cache.get(1);//返回1,由于最近使用了1,所以cache=[(1,1),(2,2)]
cache.put(3,3);
//放入,由于容量已满,所以删除久未使用的的内容(即队尾数据)
//然后将新数据插入队头
//cache=[(3,3),(1,1)]
cache.get(2);//2不存在,返回-1,未找到
cache.put(1,4);
//cache=[(1,4),(3,3)]
//键1已经存在,把原始值1覆盖为4,同时要把键值对提前到队头

算法设计

cache数据结构的必要条件:

  • 元素必须有时序,以区分最近使用的和久未使用的的数据,当容量满后要删除最久未使用的元素腾出来位置
  • 在cache中快速找到某个key是否已存在并得到对应的val
  • 每次访问cache的key需要将这个元素变为最近使用的,要支持在任意位置快速插入和删除元素

即,哈希链表,是哈希表和双向链表的结合体。

代码实现

(先自己实现LRU,便于理解)
链表节点:

class Node{
    public int key,val;
    public Node next,prev;
    public Node(int k,int v){
        this.key=k;
        this.val=v;
    }
}

双链表:

class DoubleList{
    //头尾虚节点
    private Node head,tail;
    //链表元素数
    private int size;
    
    public DoubleList(){
        //初始化双向链表的数据
        head = new Node(0,0);
        tail = new Node(0,0);
        head.next = tail;
        tail.prev = head;
        size=0;
    }
    //在链表尾部添加节点x
    public void addLast(Node x){
        x.prev = tail.prev;
        x.next = tail;
        tail.prev.next = x;
        tail.prev = x;  
        size++;
    }
    //删除链表的x节点
    public void remove(Node x){
        x.prev.next = x.next;
        x.next.prev = x.prev;
        size--;
    }
    //删除链表中第一个节点,并返回该节点
    public Node removeFirst(){
        if(head.next == tail) return null;
        Node first = head.next;
        remove(first);
        return first;
    }
    //返回链表长度
    public int size(){return size;}
}

靠尾部的数据是最近使用的,靠头部的数据是最久未使用的

class LRUCache{
    //key->Node(key,val)
    private HashMap<Integer,Node> map;
    //Node(k1,v1)<->Node(k2,v2)
    private DoubleList cache;
    private int cap;
    public LRUCache(int capacity){
        this.cap=capacity;
        map = new HashMap<>();
        cache = new DoubleList();
    }
    //将某个key提升为最近使用的
    private void makeRecently(int key){
        Node x = map.get(key);
        //先删除这个节点
        cache.remove(x);
        //重新查到队尾
        cache.addLast(x);
    }
    //添加最近使用的元素
    private void addRecently(int key,int val){
        Node x = new Node(key,val);
        cache.addLast(x);
        map.put(key,x);
    }
    //删除某个key
    private void deleteKey(int key){
        Node x = map.get(key);
        cache.remove(x);
        map.remove(key);
    }
    //删除最久未使用的的元素
    private void removeLeastRecently(){
        Node deleteNode = cache.removeFirst();
        int deleteKey = deleteNode.key;
        map.remove(deleteKey);
    }
    public int get(int key){
        if(!map.containsKey) return 01;
        makeRecently(key);
        return map.get(key).val;
    }
    public void put(int key,int val){
        if(map.containsKey(key)){
            //删除旧元素
            deleteKey(key);
            //新插入的元素为最近使用的数据
            addRecently(key,val);
            return;
        }
        if(cap == cache.size()) removeLeastRecently();
        addRecently(key,val);
    }
}

(利用java自带库)
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值