Java小白手撕LRU算法,竟讲的这么通透.

什么是LRU

LRU全称是Least Recently Used,即最近最久未使用的意思

LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰


图示

映射关系

 

增删改

 


代码问题解释

我们基本实现原理是通过Hashmap+双链表(Entry)实现的.

  • 为什么用hashmap+双链表,而不是单独的双链表?
    • 原因:用hashmap找双链表比较快,容易定位到双链表的具体的单节点,进行操作;而不是每次修改或者删除的时候都需要遍历整条链表
  • 为什么定义head节点和tail节点?
    • 原因:用head节点和tail节点更容易操作,尤其是在删除的时候

具体实现方法

  1. 首先需要编写一个Entry类,Entry类里面含有key,value值,还有前驱结点pre和后继节点next,重写构造函数

  2. 其次,在我们的LRUCache类定义一个head节点和tail节点,以及int一个容量池大小capacity和记录我们的链表的长度size,还有一个Hashmap<Integer,Entry>的cache

  3. 我们定义一个initLinkedList()方法,初始化我们的链表,使头尾节点相连

  4. 重写LRUCache的构造函数,赋值capacity并定义Hashmap容量池大小,size=0,调用initLinkedList()方法

  5. 定义addNode()和deleteNode()方法,每次添加节点都添加到头结点之后,删除的话让该几点的前后节点相连

  6. 定义moveToHead()方法,里面包裹着deleteNode()和addNode()方法,方便我们修改已存在的值(因为我们修改已存在的值的时候,需要删除该节点,然后在添加该节点)

  7. 定义get()方法,从hashmap表里查找该key存不存在,如果存在,就调用moveToHead()方法移至头结点,不存在就返回null

  8. 接下来定义put()方法,这个比较难,分情况

    1. 首先我们先get()一下,查看有没有该Entry节点

      1. 如果有该Entry节点,那么我们就重新赋该Entry节点的值,调用moveToHead()方法,返回,结束方法

      2. 如果没有该Entry节点,就new一个新的Entry节点,赋值key,value,调用addNode()方法,并放入Hashmap,size++

      3. 如果size==capacity,也就是链表长度已满,把最后一个节点(不是tail节点,是tail.pre)删掉(因为最后一个节点不常用,所以删掉),并把Hashmap中的该节点也删掉,size--,之后执行上面第2部操作

  9. 测试得出以下语句

public static void main(String[] args) {
        LRUCache cache1 = new LRUCache(2);
        cache1.put(1, 1);
        cache1.put(2, 2);

        System.out.println("get(1)之后的头结点之前的节点: " + cache1.head.next);
        System.out.println("get(1)节点: " + cache1.get(1));
        System.out.println("get(1)之后的头结点之后的节点: " + cache1.head.next);

        cache1.put(3, 3);
        System.out.println("put(3)之后的头结点之后的节点: " + cache1.head.next);

        System.out.println("get(2)节点: " + cache1.get(2));
        System.out.println("get(2)之后的头结点之后的节点: " + cache1.head.next);
    }

        //最终输出
        //get(1)之后的头结点之前的节点: Entry{key=2, value=2}
        //get(1)节点: 1
        //get(1)之后的头结点之后的节点: Entry{key=1, value=1}
        //put(3)之后的头结点之后的节点: Entry{key=3, value=3}
        //get(2)节点: null
        //get(2)之后的头结点之后的节点: Entry{key=3, value=3}

源码

package com.heaboy.mvc;

import java.util.HashMap;
import java.util.Map;

public class LRUCache {
    Entry head, tail;
    int capacity;
    int size;
    Map<Integer, Entry> cache;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        initLinkedList();
        size = 0;
        cache = new HashMap<>(capacity);
    }

    public void put(int key, int value) {
        Entry node = cache.get(key);
        if (node != null) {
            node.value = value;
            moveToHead(node);
            return;
        }
        if (size == capacity) {
            Entry lastNode = tail.pre;
            deleteNode(lastNode);
            cache.remove(lastNode.key);
            size--;
        }

        Entry newNode = new Entry(key, value);
        addNode(newNode);
        cache.put(key, newNode);
        size++;
    }

    public Integer get(int key) {
        Entry node = cache.get(key);
        if (node == null) {
            return null;
        }
        moveToHead(node);
        return node.value;
    }

    private void moveToHead(Entry node) {
        deleteNode(node);
        addNode(node);
    }

    public void addNode(Entry node) {
        head.next.pre = node;
        node.next = head.next;

        node.pre = head;
        head.next = node;
    }

    private void deleteNode(Entry node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

    private void initLinkedList() {
        head = new Entry();
        tail = new Entry();

        head.next = tail;
        tail.pre = head;
    }


    private static class Entry {
        int key;
        int value;
        Entry pre;
        Entry next;

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

        public Entry() {
        }

        @Override
        public String toString() {
            return "Entry{" +
                    "key=" + key +
                    ", value=" + value +
                    '}';
        }
    }

    public static void main(String[] args) {
        LRUCache cache1 = new LRUCache(2);
        cache1.put(1, 1);
        cache1.put(2, 2);

        System.out.println("get(1)之后的头结点之前的节点: "+cache1.head.next);
        System.out.println("get(1)节点: "+cache1.get(1));
        System.out.println("get(1)之后的头结点之后的节点: "+cache1.head.next);

        cache1.put(3, 3);
        System.out.println("put(3)之后的头结点之后的节点: "+cache1.head.next);

        System.out.println("get(2)节点: "+cache1.get(2));
        System.out.println("get(2)之后的头结点之后的节点: "+cache1.head.next);
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值