LRU算法:原理实现

什么是LRU算法?

百度百科:

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

总结来说就是末尾淘汰!目前根据自己的知识储备lru算法运用于redis缓存淘汰策略,mysql的缓存清理策略

也是根据最近刷LeeCode看到的一套题

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

如何实现?

1、通过LinkedHashMap实现

1)代码+测试脚本(有点low)

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

class LRUCache {
    private final Map<Integer, Integer> cacheMap;

    public LRUCache(int capacity) {
        int length = (int) Math.ceil(capacity / 0.75);
        cacheMap = new LinkedHashMap<Integer, Integer>(length, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                return cacheMap.size() > capacity;
            }
        };
    }

    public int get(int key) {
        return cacheMap.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        cacheMap.put(key, value);
    }

    public static void main(String[] args) {
        int length, key;
        Scanner sc = new Scanner(System.in);
        System.out.print("length=");
        length = sc.nextInt();
        System.out.println();
        LRUCache cache = new LRUCache(length);
        initMap(sc, cache);
        while (true) {
            System.out.println();
            System.out.print("get=");
            key = sc.nextInt();
            if (key == 0) {
                break;
            }
            int i = cache.get(key);
            System.out.println();
            System.out.println("key=" + key + ",value=" + i);
        }
        initMap(sc, cache);
        System.out.println("=====");
        cache.cacheMap.forEach((k, v) -> System.out.println(k + ":" + v));
    }

    private static void initMap(Scanner sc, LRUCache cache) {
        int key;
        int value;
        while (true) {
            System.out.print("key=");
            key = sc.nextInt();
            if (key == 0) {
                break;
            }
            System.out.println();
            System.out.print("value=");
            value = sc.nextInt();
            cache.put(key, value);
        }
    }
}

运行结果:

length=2

key=1

value=1
key=2

value=2
key=0

get=1

key=1,value=1

get=0
key=3

value=3
key=0
=====
1:1
3:3

Process finished with exit code 0

长度为2 分别输入[1,1] [2,2] 输入0结束,查询key=1 输入0结束,再存入[3,3] 输入0结束 最终打印[1,1][3,3]

因为会涉及到扩容问题,所以在初始化的时候根据加载因子先根据输入的长度计算map大小再初始化

复杂度分析

时间复杂度:对于 put 和 get 操作复杂度是 O(1)O(1),因为有序字典中的所有操作:get/in/set/move_to_end/popitem(get/containsKey/put/remove)都可以在常数时间内完成。
空间复杂度:O(capacity)O(capacity),因为空间只用于有序字典存储最多 capacity + 1 个元素。

方法 2:哈希表 + 双向链表

这个问题可以用哈希表,辅以双向链表记录键值对的信息。所以可以在 O(1)O(1) 时间内完成 put 和 get 操作,同时也支持 O(1)O(1) 删除第一个添加的节点。

使用双向链表的一个好处是不需要额外信息删除一个节点,同时可以在常数时间内从头部或尾部插入删除节点。

一个需要注意的是,在双向链表实现中,这里使用一个伪头部和伪尾部标记界限,这样在更新的时候就不需要检查是否是 null 节点。

实现

class LRUCache extends LinkedHashMap<Integer, Integer>{
    private int capacity;
    
    public LRUCache(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key) {
        return super.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
}


复杂度分析

时间复杂度:对于 put 和 get 都是 O(1)O(1)。
空间复杂度:O(capacity)O(capacity),因为哈希表和双向链表最多存储 capacity + 1 个元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值