LRU算法java实现

Posted 大树的困惑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LRU算法java实现相关的知识,希望对你有一定的参考价值。

LRU算法java实现

LRU算法是一种缓存淘汰策略

比如我们的电脑或者手机,在内存有限的情况下,它仅会将最近使用过的文档,文件,程序缓存在内存中…

以及我们手机中后台应用的排序,也是更具最新使用情况进行排序的

Leetcode中有关于LRU的题目,基于这道题进行探讨

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

考虑使用什么数据结构:

get,put 既要查询,又要写入

哈希表查找快

链表有顺序之分,插⼊删除快,但是查找慢。

所以将两者有效的结合成哈希链表

单向链表:单向链表的java简单实现
双向链表:双向链表的java简单实现

我们需要分析什么情况下会影响排序

1.当你新增节点时,该节点则是优先级最高的

2.当你获取/查询某个节点时,该节点优先级也要变成最高的

什么情况下会影响整体的数据量

1.当你新增节点时

2.当你删除节点时

3.当你容量满的时候,你新增节点的时候,需要删除优先级最低的哪个节点

这里使用双向链表中的head,tail 头节点/和尾节点这样的逻辑结构引用作为优先级的代表

当节点处于tail时,优先级最低

当节点处于head时,优先级最高

下面看下代码的实现

package DataStrct.LRU;

import java.util.HashMap;

/**
 * @author luke
 * @date 2021/4/1821:09
 */
public class LRUDemo 
    public static void main(String[] args) 
//        DoubleLinklist doubleLinklist = new DoubleLinklist(new Node(1, 1));
//        doubleLinklist.addfirst(new Node(2, 2));
//        doubleLinklist.addfirst(new Node(3, 3));
//        doubleLinklist.addfirst(new Node(4, 4));
//        doubleLinklist.addfirst(new Node(5, 5));
//        doubleLinklist.print_list();
//        System.out.println(doubleLinklist.size());
//        System.out.println("---------------");
//        doubleLinklist.del_last();
//        doubleLinklist.print_list();
//        System.out.println("-----------del");
//        doubleLinklist.del(new Node(4,2));
//        doubleLinklist.print_list();
        LRU cache = new LRU(1);
        cache.put(2, 1);
        System.out.println(cache.get(2));
//        cache.put(2, 2);
//        System.out.println(cache.get(1));       // 返回  1
//        cache.put(3, 3);    // 该操作会使得密钥 2 作废
//        System.out.println(cache.get(2));       // 返回 -1 (未找到)
//        cache.put(4, 4);    // 该操作会使得密钥 1 作废
//        System.out.println(cache.get(1));       // 返回 -1 (未找到)
//        System.out.println(cache.get(3));       // 返回  3
//        System.out.println(cache.get(4));       // 返回  4

    

    static class LRU
        //hash表
         HashMap<Integer,Node> map;//hash表
         DoubleLinklist dbl;//存储节点的双向链表
         int cap;//缓存的容量

        //初始化

        public LRU(int cap) 
            this.cap = cap;
            map = new HashMap<>();
            dbl= new DoubleLinklist();
        

        /**
         * 1.get () 获取某个节点 -->对应的该节点会被提到优先级最高
         * 2.put () 添加某个节点 -->添加的节点优先级最高
         */

        public int get(int key)
            if (!map.containsKey(key))//map用于维护已添加有效的节点,查询快
                return -1;
            
            //获取节点值
            int value = map.get(key).value;
            //对应的节点提到最高级别
            put(key,value);
            return value;
        

        public void put (int key ,int  value )
            //根据传入key value 构建新的节点
            Node node = new Node(key, value);
            //添加新节点先判断是否存在改节点
            if(map.containsKey(key))
                //如果以存在,删除旧节点,将新节点添加到头部
                map.remove(key);
                map.put(key,node );
                dbl.del(node);
                dbl.addfirst(node);
            else if(!map.containsKey(key))
                //如果不存在该节点的话说明是新增节点,新增节点要判断容量的问题
                int size = dbl.size();
                if (size==cap)
                    //如果当前的链表长度和容量上限相等时,需要剔除链表中最后一位
                    Node lastnode = dbl.del_last();
                    if(lastnode!=null)
                    map.remove(lastnode.key);
                    //并将新节点添加到链表的头部
                    dbl.addfirst(node);
                    map.put(key,node );
                else 
                    dbl.addfirst(node);
                    map.put(key,node );
                
            

        
    

    /**
     * 节点类
     */
    static class Node 
        int key;
        int value;
        Node next, pre;

        public Node(int key, int value) 
            this.key = key;
            this.value = value;
        

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

    static class DoubleLinklist 

        Node head;
        Node tail;

        public DoubleLinklist() 
        

        /**
         * 初始化链表
         *
         * @param node
         */


        public DoubleLinklist(Node node) 
            this.head = node;
            this.tail = node;

        

        /**
         * 1.头插
         * 2.尾插
         * 3.删除最后一个节点   用于删除当添加的节点达到容量上限时删除优先级最低的
         * 4.删除节点
         * 5.链表长度
         */
        /**
         * 1.头插 新节点从链表的头节点插入
         *
         * @param node
         */
        public void addfirst(Node node) 
            if (head == null) 
                //当链表为空时
                head = node;
                tail=node;
                return;
            
            head.pre = node;
            node.next = head;
            head = node;
        

        /**
         * 尾插 新加入的节点从链表的尾节点加入
         *
         * @param node
         */
        public void addtail(Node node) 
            if (tail == null) 
                //当链表为空时
                head = node;
                tail = node;
                return;
            
            //将插入节点赋值给tail节点---tail节点的下个节点指向插入节点,插入节点的上个节点指向tail节点的上个节点
            tail.next = node;
            node.pre = tail;
            tail = node;//更新链表的尾部节点
        
        public void del (Node node )
            //更具id进行删除
            Node temp = head;
            //如果是空表直接返回
            if (head == null) 
                System.out.println("当前为空链表");
                return;
            
            while (true) 
                if (temp.next == null) 
                    break;//遍历到最后了
                
                if (temp.key == node.key) 
                    break;
                
                temp = temp.next;
            
            //删除操作,
            // 当前节点的上个节点的下个节点指向当前节点的下个节点
            // 当前节点的下个节点的上个节点指向当前节点的上个节点
            //当删除节点是头节点或者尾节点时需要特殊处理
            if (temp.key == head.key) 
                head=head.next;
                if (head!=null)
                head.pre=null;
            
            else if (temp.key == tail.key ) 
                tail = tail.pre;
                if (tail!=null)
                tail.next = null;
            
            else 
                temp.pre.next=temp.next;
                temp.next.pre=temp.pre;
            

        

        public Node del_last()
            if (tail==null)
                return null;
            
            Node temp=tail;
            if(tail.pre!=null)
            tail.pre.next=null;
            tail=tail.pre;
            return temp;
        


        public int size() 
            int num=0;
            Node temp=head;
            if (head==null)
                //列表为空
                return 0;
            
            while(true)
                if (temp==null)
                    break;
                
                num+=1;
                temp=temp.next;
            
            return num;
        

        /**
         * 打印链表
         */
        public void print_list() 
            //遍历链表
            Node tempnode = head;
            while (true) 
                System.out.println(tempnode);
                if (tempnode.next == null) 
                    break;
                
                tempnode = tempnode.next;
            
        
    


以上是关于LRU算法java实现的主要内容,如果未能解决你的问题,请参考以下文章

Java手写原生的LRU算法

LRU 缓存淘汰算法

LRU算法

面试题LRU算法及编码实现LRU策略缓存

使用LinkedHashMap实现LRU算法

JavaScript算法题实现-146-LRU缓存机制——腾讯面试题库