Leetcodet题解——LRU缓存

77 阅读2分钟

这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

前言

大家好,我是程序猿小白 GW_gw,很高兴能和大家一起学习进步。

以下内容部分来自于网络,如有侵权,请联系我删除,本文仅用于学习交流,不用作任何商业用途。

摘要

本文主要带大家了解leetcode第146题LRU缓存的解法。

题目

image.png

image.png

image.png

解法

首先我们先了解一下什么是LRU,有助于理解思路。

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法。 该算法为每个页面创建一个访问字段,记录上次该页面被访问到现在的时间T,当需要淘汰页面时,选择t最大的进行淘汰。

再来看一下题目要求,get时间复杂度要求为O(1),我们不难想到使用hashMap。 put的时间复杂度为O(1),我们可以使用双向链表来实现,靠近头节点是最近使用的,靠近尾节点是最久未使用的。

接着我们来分析get和put操作的实现。

get:

1. 如果key存在,更新key的value,把该节点移动到头节点。
2. 如果key不存在,返回-1.

put:

1. 如果key存在,更新key的value,把该节点移动到头节点。
2. 如果key不存在,创建新节点,并插入到链表头部。接着判断是否超出容量,如果超出容量,删除尾节点。

细节优化:伪造头节点和尾节点这样就不需要判断相邻节点是否存在。

通过上面分析,我们需要经常使用到把节点移动到头部、删除尾节点操作我们可以进行封装尾方法,方便我们调用。

//删除节点
private void removeNode(DLinkedNode dNode){
    //该节点的前一个节点指向该节点的后一个节点
    dNode.prev.next = dNode.next;
    dNode.next.prev = dNode.prev;
}
//在头部添加节点
private void addToHead(DLinkedNode dNode){
    dNode.prev = head;
    dNode.next = head.next;
    dNode.next.prev = dNode;
    dNode.next = dNode;
}
//移动节点到头部
private void moveToHead(DLinkedNode dNode){
    removeNode(dNode);
    addToHead(dNode);
}
//删除尾节点
private DLinkedNode removeTail(){
    DLinkedNode res = tail.prev;
    removeNode(res);
    return res;
}

get和put方法实现:

class DLinkedNode{
    int key;
    int value;
    DLinkedNode prev;
    DLinkedNode next;

    public DLinkedNode() {
    }

    public DLinkedNode(int key, int value) {
        this.key = key;
        this.value = value;
    }
}
private Map<Integer,DLinkedNode> map = new HashMap<>();
private int capacity;
private int size;
private DLinkedNode head,tail;
public LRUCache(int capacity) {
   this.size = 0;
   this.capacity = capacity;
   //伪造头节点和尾节点
    head = new DLinkedNode();
    tail = new DLinkedNode();
    head.next = tail;
    tail.prev = head;
}

public int get(int key) {
    DLinkedNode node = map.get(key);
    if(node==null){
        return -1;
    }
    moveToHead(node);
    return node.value;
}

public void put(int key, int value) {
    DLinkedNode node = map.get(key);
    if(node==null){
        //如果不包含该key,就添加进去
        DLinkedNode newNode = new DLinkedNode(key,value);
        map.put(key,newNode);
        addToHead(newNode);

        size++;
        //之后判断是否超出容量
        if(size>capacity){
            DLinkedNode tail = removeTail();
            size--;
            map.remove(tail.key);
        }
    }
    else{
        //如果包含直接更新,并修改位置
        node.value = value;
        moveToHead(node);
    }
}

小结

以上就是关于Leetcode的146题LRU缓存的解法的一些介绍,希望能对读者有所帮助,如有不正之处,欢迎留言指正。