面试题 16.25. LRU 缓存

面试题 16.25. 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

方法一:

  • 创建一个Map -map保存 <key,val>
  • 创建一个Map-time保存 <key,time>
  • get(key)时
    • 如果存在,更新time(key,0),其余的key对应的time-1
    • 如果不存在,返回-1
  • put(key,val)时
    • 未未满时
      • 是否存在
        • 如果存在,更新(key,val),更新time(key,0),其余的key对应的time-1
        • 如果不存在, 添加map(key,val),添加time(key,0),其余的key对应的time-1;
    • 满时
      • 是否存在
        • 如果存在,更新(key,val),更新time(key,0),其余的key对应的time-1
        • 如果不存在,删除time中最小的val,删除map对应的key,添加map(key,val),添加time(key,0),其余的key对应的time-1;

package 力扣;

import java.util.HashMap;
// 实现了但是超时
public class LRUCache {
    public static void main(String[] args) {
        LRUCache lruCache=new LRUCache(2);
        System.out.println(lruCache.get(2));
        lruCache.put(2,6);
        System.out.println(lruCache.get(1));
        lruCache.put(1,5);
        lruCache.put(1,2);
        System.out.println(lruCache.get(1));
        System.out.println(lruCache.get(2));
    }

    HashMap<Integer,Integer> map=new HashMap<>();
    HashMap<Integer,Integer> time=new HashMap<>();
    int capacity;
    int usedCapacity;

    public LRUCache(int capacity) {
         this.capacity=capacity;
         usedCapacity=0;
    }





    public int get(int key) {
        if (map.containsKey(key)) {
            // 更新值
            changeTime(key);
            return map.get(key);
        }
        return -1;
    }

    public void changeTime(Integer key){
        // 更新值
        if(time.isEmpty()){
            time.put(key,0);
        }else {
            for (Integer integer : time.keySet()) {
                time.put(integer,time.get(integer)-1);
            }
            time.put(key,0);
        }
    }

    public Integer getLRU(){
        int min=1;
        int key=0;
        for (Integer integer : time.keySet()) {
           if(time.get(integer)<min){
               min=time.get(integer);
               key=integer;
           }
        }
        return key;
    }

    public void put(int key, int value) {
        // 情况一 容量尚未占满
        if(usedCapacity<capacity){
            if(map.containsKey(key)){
                map.put(key,value);
                // 更新值
                changeTime(key);
            }else {
                map.put(key,value);
                usedCapacity++;
                // 更新值
                changeTime(key);
            }
        }else {
            // 情况二 容量已经被占满 需要删除一个最近未使用的
            if(usedCapacity==capacity){
                if(map.containsKey(key)){
                    map.put(key,value);
                    // 更新值
                    changeTime(key);
                }else{
                    Integer lruKey = getLRU();
                    map.remove(lruKey);
                    time.remove(lruKey);
                    map.put(key,value);
                    //更新值
                    changeTime(key);
                }


            }
        }

    }
}

方法二:

  • 使用双向链表
    • Node {int key,int val,Node pre,Node next}

在这里插入图片描述

package 力扣;

// 上一个方法太笨了の 这里使用最基础的双向链表实现

import javax.sound.midi.Soundbank;
import java.time.temporal.ValueRange;

public class LRUCache2 {
    public static void main(String[] args) {
        LRUCache2 cache = new LRUCache2( 2 /* 缓存容量 */ );

        cache.put(1, 1);
        cache.put(2, 2);
        System.out.println(cache.get(1));
        cache.put(3, 3);    // 该操作会使得密钥 2 作废
        System.out.println(cache.get(2));
        cache.put(4, 4);    // 该操作会使得密钥 1 作废
        System.out.println(cache.get(1));
        System.out.println(cache.get(3));
        System.out.println(cache.get(4));


    }
    LinkList link;
    Integer capacity;
    Integer usedCapacity;

    public LRUCache2(Integer capacity) {
        this.capacity = capacity;
        usedCapacity=0;
        link=new LinkList();

    }
    public int get(int key) {
        // 获取这个数的值 ,先删除它,在添加他,这样他就在首位,删除时他是不是最后一个
       return link.getVal(key);
    }


    public void put(int key, int value) {

        //容量未满
        if(usedCapacity<capacity){
            // 更新数值或者添加
            link.NoFullADD(key,value);
            usedCapacity++;
        }
        //容量满了
        else {
            // 容量满了的条件下,更新数值或者添加
            link.FullADD(key,value);
        }


    }
}

class LinkList{
    Node head;
    Node end;

    public LinkList() {
        head=new Node();
    }
    public void bl(){
        Node p=head.next;
        System.out.print("当前链表:");
        while (p!=null){
            System.out.print("key:"+p.key+"-value:"+ p.val+",");
        }
        System.out.println();
    }

    public void add(int key,int val){
        if(head.next==null){
            Node p=new Node(key,val);
            head.next=p;
            p.pre=head;
            end=p;
        }
        else {
            Node p=new Node(key,val);
            Node temp=head.next;
            head.next=p;
            p.next=temp;
            p.pre=head;
            temp.pre=p;
        }
    }


    // 获取这个数,并更新这个数在链表的位置
    public int getVal(int key) {
        Node p=head.next;
        while(p!=null){
            //假如找到了
            if(p.key==key){
                int val=p.val;
                // 如果P在末尾,不能直接删除需要更改end位置
                if(p.next==null){
                    end=p.pre;
                    end.next=null;
                    add(key,val);
                    return val;
                }
                //P不在末尾
                else {
                    Node pre=p.pre;
                    Node next = p.next;
                    pre.next=next;
                    next.pre=pre;
                    add(key,val);
                    return val;
                }
            }
            p=p.next;
        }
        //找不到
        return -1;
    }
    // 容量未满时添加 注意:需要判断是更新还是添加
    public void NoFullADD(int key, int value) {
        Node p=head.next;
        while (p!=null){
            // 找到了 为更新,首先要删除他,在添加他
            if(p.key==key){
                //如果为末尾节点,记得更新end位置
                if(p.next==null){
                    end=p.pre;
                    end.next=null;
                    add(key,value);
                    return;
                }
                //如果为中间节点,
                else {
                    Node pre = p.pre;
                    Node next = p.next;
                    pre.next=next;
                    next.pre=pre;
                    add(key,value);
                    return;
                }
            }
            p=p.next;
        }

        //未找到,直接添加
        add(key,value);

    }

    //此时链表已满
    public void FullADD(int key, int value) {
        Node p =head.next;
        //如果P已经存在
        while(p!=null){
            if(p.key==key){
                //如果为末尾节点,记得更新end位置
                if(p.next==null){
                    end=p.pre;
                    end.next=null;
                    add(key,value);
                    return;
                }
                //如果为中间节点,
                else {
                    Node pre = p.pre;
                    Node next = p.next;
                    pre.next=next;
                    next.pre=pre;
                    add(key,value);
                    return;
                }

            }
            p=p.next;
        }
        // 如果P不存在,需要删除最后一个节点,在添加
        end=end.pre;
        end.next=null;
        add(key,value);
    }
}


class Node{
    Node pre;
    Node next;
    Integer key;
    Integer val;

    public Node() {
    }

    public Node(Integer key,Integer val) {
        this.key= key;
        this.val = val;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值