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;
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面是一个简单的 Java 实现,使用 LRU(Least Recently Used)算法计算页面置换的缺页率和缺页序列: ```java import java.util.*; public class LRUPageReplacement { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("请输入内存块数:"); int numFrames = sc.nextInt(); int[] frames = new int[numFrames]; Arrays.fill(frames, -1); // 初始化为无效页 Map<Integer, Integer> pageLastUsed = new HashMap<>(); // 记录页最近使用时间 int numFaults = 0; // 缺页次数 List<Integer> pageFaults = new ArrayList<>(); // 记录缺页序列 System.out.print("请输入页面引用串(以空格分隔):"); String[] refStr = sc.next().split("\\s+"); for (String ref : refStr) { int page = Integer.parseInt(ref); boolean found = false; for (int i = 0; i < numFrames; i++) { if (frames[i] == page) { found = true; break; } } if (!found) { int leastUsedPage = -1; long minLastUsed = Long.MAX_VALUE; for (int i = 0; i < numFrames; i++) { int f = frames[i]; long lastUsed = pageLastUsed.getOrDefault(f, 0); if (lastUsed < minLastUsed) { leastUsedPage = i; minLastUsed = lastUsed; } } frames[leastUsedPage] = page; pageLastUsed.put(page, System.nanoTime()); numFaults++; pageFaults.add(page); } else { pageLastUsed.put(page, System.nanoTime()); } } double faultRate = (double) numFaults / refStr.length; System.out.printf("缺页率:%.2f%%\n", faultRate * 100); System.out.println("缺页序列:" + pageFaults); } } ``` 运行程序,输入内存块数和页面引用串,即可输出缺页率和缺页序列。例如,输入: ``` 请输入内存块数:3 请输入页面引用串(以空格分隔):1 2 3 4 1 2 5 1 2 3 4 5 ``` 输出: ``` 缺页率:33.33% 缺页序列:[1, 2, 3, 4, 5, 1, 2, 5, 3, 4] ``` 请注意,这只是一个简单的实现,可能存在性能问题和边界情况未处理。在实际应用中,需要根据具体情况进行优化和改进。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值