zoukankan      html  css  js  c++  java
  • LRU算法

    本文 利用双链表+hashMap实现LRU算法 

    为什么要选双链表+HashMap?

    因为,LRU的核心是“最近最少使用”,主要的功能有:

    1. 在get数据时,先获取到数据,然后会将该数据放到链表最后位置,方便下次取。(get时可以先到链表尾部取,看是不是)

    2. 在put数据时,首先看链表中有没有,有就将这个数据放到链表尾部,方便下次取,没有就在链表尾部插入。

    3. 在put数据时,看存入数量满了没,满了就删除链表头部的元素(即删除最老元素)

    因此:

    使用HashMap的原因:都有查询数据的操作,使用HashMap可以快速查到有没有数据,

    使用双向链表的原因:有插入与删除的操作,选用Linked双向链表更有优势。

    代码:

    package Algorithm.LRU;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * LRU(Least Recently Used)即“最近最少使用”,这里使用双链表+hashMap实现了LRU算法
     * 注意:LRUCache中没有重复的节点
     *
     * @author chenjunjie
     * @since 2018-04-25
     */
    public class LRUCache {
        Node head, tail;
        int size;
        int capacity;
        Map<Integer,Node> map;
    
        /**
         * 定义内部类,Node节点
         */
        static class Node {
            int key;
            int val;
            Node pre;
            Node post;
    
            /**
             * 构造函数
             * @param key Map的key
             * @param val May的value
             */
            Node(int key, int val) {
                this.key = key;
                this.val = val;
            }
        }
    
        /**
         * 构造函数,初始化
         * @param capacity
         */
        public LRUCache(int capacity) {
            this.head = null;
            this.tail = null;
            this.size = 0;
            this.capacity = capacity;
            // 使用了HashMap
            // 注意这里Map中的value为Node类型,这样可以方便索引到某个具体node
            this.map = new HashMap<Integer, Node>();
        }
    
        /**
         * 获取最近使用node
         * 先remove掉节点以前所处位置,然后添加到链表的尾部,从而达到最近使用的效果
         * @param key
         * @return 返回value
         */
        public int get(int key) {
            if(!map.containsKey(key)) {
                return -1;
            }
            Node temp = map.get(key);
            remove(temp);
            add(temp);
            return temp.val;
        }
    
        /**
         * 添加新节点,并保证放入最后节点
         * @param key map的key值
         * @param value map中的value
         */
        public void put(int key, int value) {
            Node temp;
            // 如果之前有key,但不是放在链表尾部,则先删除然后添加到尾部
            if(map.containsKey(key)){
                temp = map.get(key);
                remove(temp);
                map.remove(key);
                size--;
            }
    
            // 如果超过了容量,这删除头部节点,即删除“最老”节点
            if(size == capacity){
                Node h = head;
                remove(h);
                map.remove(h.key);
                size--;
            }
    
            temp = new Node(key,value);
            // 链表中添加Node
            add(temp);
            // HashMap添加Entry
            map.put(key,temp);
            size++;
        }
    
        /**
         * 普通的linked链表删除操作
         * @param n 要删除的节点
         */
        private void remove(Node n) {
            // 如果只有一个节点
            if(n==head && n==tail){
                head = null;
                tail = null;
            }
            // 如果是头节点
            else if(n == head) {
                head = head.post;
                head.pre = null;
            }
            // 如果是尾节点
            else if(n == tail) {
                tail = tail.pre;
                tail.post = null;
            }
            //
            else{
                n.pre.post = n.post;
                n.post.pre = n.pre;
            }
            n.pre = null;
            n.post = null;
        }
    
        /**
         * 在链表尾部加入节点
         * @param n 添加节点
         */
        private void add(Node n) {
            if(head == null && tail == null) {
                head = n;
                tail = n;
            }else {
                tail.post = n;
                n.pre = tail;
                tail = n;
            }
        }
    
        /**
         * getMap,方便测试遍历查询结果
         * @return
         */
        public Map<Integer, Node> getMap() {
            return map;
        }
    
        /**
         * getHead,方便测试遍历查询结果,实际上不要暴露出来
         * @return
         */
        public Node getHead(){
            return head;
        }
    
    
    }

    测试:

    public static void main (String[] args) {
        LRUCache lurCache = new LRUCache(10);
        int N = 12 ; //7
        int i = 1;
        while (i<N){
            lurCache.put(i,i*2);
            i++;
        }
    
        System.out.println(lurCache.get(4));
        lurCache.put(4,666);
    
        // 遍历map
        Map hsMap = lurCache.getMap();
        Iterator it = hsMap.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<Integer,LRUCache.Node> result = (Map.Entry<Integer,LRUCache.Node>)it.next();
            System.out.printf("(key=%d,value=%d)
    ",result.getKey(),result.getValue().val);
        }
    
        System.out.println("-----------");
    
        // 遍历node
        LRUCache.Node n = lurCache.getHead();
        LRUCache.Node cur = n;
        while(cur != null){
            System.out.printf("(key=%d,value=%d)
    ",cur.key,cur.val);
            cur = cur.post;
        }
    }

    结果如下:

    特性:

    1. 插入了12条数据,由于定义的容量为10,因此每次满容量时删除头结点的数据。

    2. 修改key=4的值,修改后放到队列尾部

    3. 使用get()后,node节点的数据也会放到队列尾部(ps:测试代码中没有写)

    扩展(待续):

    /**
    * LRU是Least Recently Used 的缩写,即“最近最少使用”
    * 扩展 LinkedHashMap 可以实现就实现了LRU,
    * LinkedHashMap中最近读取的会放在最前面,最最不常读取的会放在最后,同时
    * 调用removeEldestEntry会移除链表中“最老”的节点,但注意源码中调用该方法返回的值为false
    * 因此,可以继承LinkedHashMap重写removeEldestEntry, 从而构建LRU缓存
    */
  • 相关阅读:
    北京燃气IC卡充值笔记
    随机分析、随机控制等科目在量化投资、计算金融方向有哪些应用?
    量化交易平台大全
    Doctor of Philosophy in Computational and Mathematical Engineering
    Institute for Computational and Mathematical Engineering
    Requirements for the Master of Science in Computational and Mathematical Engineering
    MSc in Mathematical and Computational Finance
    万字长文:详解多智能体强化学习的基础和应用
    数据处理思想和程序架构: 使用Mbedtls包中的SSL,和服务器进行网络加密通信
    31-STM32+W5500+AIR202/302基本控制篇-功能优化-W5500移植mbedtls库以SSL方式连接MQTT服务器(单向忽略认证)
  • 原文地址:https://www.cnblogs.com/chenjunjie12321/p/8945246.html
Copyright © 2011-2022 走看看