LRU 算法简单实现

最近看innoDb缓存池实现的时候,提到了LRU的算法,后面看了下memcached的实现也提到了LRU算法,正好leetcode146题,就是讲这个LRU算法的,所以就写个简单实现。

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。

  1. insert(val):当元素 val 不存在时,向集合中插入该项。
  2. remove(val):元素 val 存在时,从集合中移除该项。
  3. getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。

示例 :

// 初始化一个空的集合。
RandomizedSet randomSet = new RandomizedSet();

// 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(1);

// 返回 false ,表示集合中不存在 2 。
randomSet.remove(2);

// 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomSet.insert(2);

// getRandom 应随机返回 1 或 2 。
randomSet.getRandom();

// 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomSet.remove(1);

// 2 已在集合中,所以返回 false 。
randomSet.insert(2);

// 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
randomSet.getRandom();

这里我选择使用hashmap和一个链表来实现。hashmap用来存储节点,链表来维护节点先后顺序。

LRU算法每次取数据时,先判断hashmap中是不是有这个节点,如果有就返回该节点维护的value,并且将该节点的顺序放到链表的最前面,所以最开始新增两个辅助节点,头节点和尾节点。

LRU算法每次存数据时,先判断hashmap中有没有该key的节点,如果有,就更新该key的值,同时将该节点的是顺序放到链表的最前面。

这里有个注意的点,链表的容量如果是限定的,那么就需要考虑这次添加新的节点,会不会导致hashmap的size会超过链表的容量。如果超过,那么就要去掉链表最后的节点,也就是该节点的key对应的hashmap中的内容。

假设,当前hashmap的size已经到达了链表的初始容量,那么这时候新插入值就会有三种情况:

1. 新值在hashmap中并没有

2. 新值在hashmap中有,并且是最后一个节点

3. 新值在hashmap中有,并且非最后一个节点

这三种情况,最主要的一个要设计的点就是如果再插入会导致hashmap的size大于链表的初始容量,什么时候去删掉链表最后一个节点。

import java.util.HashMap;

/**
 * @author by csucoderlee
 * @created on 2018 08 14 16:08
 * @description LRU least recently used 最近最少使用, 如果数据最近被访问过,那么将来被访问的几率也会很高
 * 最常见的实现,维护一个链表,新数据都插入链表头,缓存命中的数据也会被移动到链表头,
 * 当链表满时,链表尾部的数据也会被丢弃
 * 缺点就是缓存命中后,需要遍历链表找到对应的数据,移动到链表头
 *
 */
public class LruTest {

    public static void main(String[] args) {

    }
}

class LRUCache {


    private HashMap<Integer, Node> map;

    private int size;

    private int capacity;

    Node head;

    Node tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.size = 0;
        this.head = new Node(0,0);
        this.tail = new Node(0,0);
        map = new HashMap<>();
        this.head.next = this.tail;
        this.tail.pre = this.head;
    }

    public int get(int key) {
        Node node = map.get(key);
        if (null == node) {
            return -1;
        }
        removeNode(node);
        addHeadNode(node);
        return node.value;
    }

    public void put(int key, int value) {
        if(map.containsKey(key)){
            Node target = map.get(key);
            target.value = value;
            removeNode(target);
            addHeadNode(target);
        }else {
            Node newNode = new Node(key,value);
            map.put(key,newNode);
            if(size>=capacity){
                map.remove(this.tail.pre.key);
                removeNode(this.tail.pre);
                this.size--;
            }
            addHeadNode(newNode);
            this.size++;
        }
    }

    private void removeNode(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
        node.pre = null;
        node.next = null;
    }

    private void addHeadNode(Node node) {
        node.next = this.head.next;
        node.next.pre = node;
        this.head.next = node;
        node.pre = this.head;
    }

    class Node {

        int key;
        int value;
        Node pre;
        Node next;

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

    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值