LeetCode146.LRU缓存机制

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

LeetCode146链接,难度中等

实现思路

  1. 采用HashMap,对key-value进行存储,获取时直接查询map,达到查询时间O(1)
  2. 采用双向链表,链表的每个节点是对key-value的包装,按照访问时间的先后进行链接,最近访问的放在链表头部,最久访问的放在链表尾部,用两个额外指针head和tail进行标记,当capacity满时,直接对tail进行删除,而不需要遍历整个链表;插入时,新节点直接放在链表头部,修改head指针即可。
  3. put时,如果已存在这个key,那么只需要更新它的value,再将其原有的指向关系修改,最后提到头部,修改head指针和节点的pre和next指针;如果不存在,先判断capacity是否已满,满了需要先删除tail节点,之后在将新节点放在表头,最后将key-Node放到map。
  4. get时,先根据map是否存在,不存在直接返回-1或null;存在,则需要更新该key在链表的位置,修改该key节点的原来指向关系,再放到头部,修改head指针和节点的pre和next指针
  5. remove时,和get类似,只不过修改该key节点的指向关系之后不需要放在链表头部
  6. clear时,将所有属性和集合置空即可
package com.tao.leetcode;

import java.util.HashMap;
import java.util.Optional;

/**
 * @author: Penger
 * @time: 2019/8/7
 * @description: <p>
 * </p>
 **/
public class CustomLru<K, V> {
    class LruNode {
        LruNode pre;
        LruNode next;
        K key;
        V value;

        LruNode(K key, V value, LruNode pre, LruNode next) {
            this.key = key;
            this.value = value;
            this.pre = pre;
            this.next = next;
            Optional.ofNullable(next).ifPresent(node -> node.pre = this);
        }
    }

    private int maxCapacity;
    private int size;
    private HashMap<K, LruNode> map;
    private LruNode head;
    private LruNode tail;

    public CustomLru(int capacity) {
        this.maxCapacity = capacity;
        this.size = 0;
        this.map = new HashMap<>(capacity);
        this.head = null;
        this.tail = null;
    }

    public void put(K k, V v) {
        Optional.ofNullable(map.get(k)).map(node -> {
            if (node == head) {
                node.value = v;
                return node;
            }
            node.value = v;
            removeNode(node);
            moveToHead(node);
            return true;
        }).orElseGet(() -> {
            if (size >= maxCapacity) {
                map.remove(tail.key);
                removeNode(tail);
            } else {
                size++;
            }
            if (head == null) {
                head = tail = new LruNode(k, v, null, null);
            } else {
                head = new LruNode(k, v, null, head);
            }
            map.put(k, head);
            return true;
        });
    }

    public V get(K k) {
        return Optional.ofNullable(map.get(k)).map(node -> {
            if (node == head) {
                return node.value;
            }
            removeNode(node);
            moveToHead(node);
            return node.value;
        }).orElse(null);
    }

    public V remove(K k) {
        return Optional.ofNullable(map.get(k)).map(node -> {
            if (node == head) {
                head = node.next;
                head.pre = null;
            }
            removeNode(node);
            size--;
            return node.value;
        }).orElse(null);
    }

    public void clear() {
        head = null;
        tail = null;
        map.clear();
    }

    private void removeNode(LruNode node) {
        Optional.ofNullable(node).ifPresent(cur -> {
            Optional.ofNullable(cur.pre).ifPresent(n -> n.next = cur.next);
            Optional.ofNullable(cur.next).ifPresent(n -> n.pre = cur.pre);
            if (cur == tail) {
                tail = cur.pre;
            }
            cur.pre = null;
            cur.next = null;
        });
    }

    private void moveToHead(LruNode node) {
        Optional.ofNullable(node).ifPresent(n -> {
            n.next = head;
            head.pre = n;
            n.pre = null;
            head = n;
        });
    }

    public static void main(String[] args) {
        CustomLru<Integer, Integer> cache = new CustomLru<>(2);
        cache.put(1, 1);
        cache.put(2, 2);
        // 返回  1
        System.out.println(cache.get(1));
        // 该操作会使得密钥 2 作废
        cache.put(3, 3);
        // 返回 null (未找到)
        System.out.println(cache.get(2));
        // 该操作会使得密钥 1 作废
        cache.put(4, 4);
        // 返回 null (未找到)
        System.out.println(cache.get(1));
        // 返回  3
        System.out.println(cache.get(3));
        // 返回  4
        System.out.println(cache.get(4));
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,248评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,681评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,443评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,475评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,458评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,185评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,451评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,112评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,609评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,083评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,163评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,803评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,357评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,357评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,590评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,636评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,925评论 2 344

推荐阅读更多精彩内容