LeetCode-146.LRU缓存机制

题目

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) — 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) — 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶: 你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

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

解题思路—双向链表+哈希表:使用双向链表与哈希表的结合,可以实现在O(1)的时间复杂度内完成LRU缓存机制。构建带有表头和表尾的双链表作为LRU索引表,并且用HashMap作为数据表来存储所有链表节点,添加或更新的节点都放在链表头,若HashMap中的个数达到容量上限时,删除链表的最后一个节点。获取数据时,直接从HashMap中get对应的key值value即可。

解题思路—使用LinkedHashMapLinkedHashMap是Map接口的哈希表和双向链表的结合,特别适合实现LRU缓存机制。
LinkedHashMap中有一个带参的构造函数:

public LinkedHashMap(int initialCapacity,  float loadFactor,  boolean accessOrder) {  
    super(initialCapacity, loadFactor);  
    this.accessOrder = accessOrder;  
}

其中:

  • initialCapacity为HashMap中桶的数量(默认为16个)
  • loadFactor为装载因子,用来判断HashMap中的桶有没有满,loadFactor的默认值为0.75f,计算装载因子的公式为 HashMap中存在的KV键值数量/capacity。当元素个数超16loadFactor也就是160.75=12时候,HashMap会先扩容2倍到32,接着还有可能会到64等。
  • accessOrder定义了排序模式,boolean型,若双向链表的排序为访问顺序从少到多,则为true;若排序为插入顺序,则为false,也就是默认值

LinkedHashMap还提供了removeEldestEntry(Map.Entry<K,V> eldest)方法,该方法可以提在每次添加新元素时移除最旧元素。方法返回默认为false,不移除旧元素。当返回值为true的时候,可以实现移除功能。方法如下:

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {  
       return false;  
} 

所以,想要实现LRU缓存机制,只要新建一个带参的LinkedHashMap,同时Override函数removeEldestEntry返回map.size>capacity,即当前map中的容量大于设定上限时,实现最旧元素移除功能。
Java解题中提供了两种代码实现方式,一个是新建一个class,还有一个是在实例化对象的时候override。

Java解题—双向链表+哈希表

import java.util.HashMap;
import java.util.Map;
class LRUCache {
// 构建双链表节点
    class Node{
        int key;
        int value;
        Node pre;
        Node next;

        public Node(int key, int value){
            this.key = key;
            this.value = value;
        }
    }

    private int capacity;
    private Node first;
    private Node last;

    private Map<Integer, Node> map;


    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.map = new HashMap<>(capacity);
    }

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

    public void put(int key, int value) {
        Node node = map.get(key);
        if(node==null){
            node = new Node(key, value);
            // 如果当前map中存放了capacity个
            // 需要删除最后一个再加入
            if(map.size()==this.capacity)
                removeLast();
            addtoHead(node);
            map.put(key, node);
        }else{
            node.value = value;
            movetoHead(node);
        }
    }
    
    // 把当前节点调置链表的第一个
    public void movetoHead(Node node){
        if(node==first)
            return;
        else if(node==last){
            last.pre.next = null;
            last = last.pre;
        }else{
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }
        node.pre = first.pre;
        node.next = first;
        first.pre = node;
        first = node;
    }
    
    // 移除链表中最后一个结点
    public void removeLast(){
        map.remove(last.key);
        Node prenode = last.pre;
        if(prenode!=null)
            prenode.next = null;
        last = prenode;
    }
    
    // 将新节点作为链表的头节点
    public void addtoHead(Node node){
        if(map.isEmpty()){
            first = node;
            last = node;
        }else{
            node.next = first;
            first.pre = node;
            first = node;
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

Java解题—使用LinkedHashMap-1

import java.util.LinkedHashMap;
import java.util.Map.Entry;

class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{
	// 缓存容量
    private int capacity;
    // 带参构造器
    public LRULinkedHashMap(int capacity){
        // accessOrder设为true,代表开启LRU缓存,该哈希映射的迭代顺序就是最后访问其条目的顺序
    		// 0.75为LinkedHashMap默认装载因子大小
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

	// 当插入后的size大于capacity时,返回ture,删除链表的顶端元素
	// macos的idea中,override快捷键为command+o,可以选择能够重载的函数
    @Override
	protected boolean removeEldestEntry(Entry<K, V> eldest) {
		return this.size() > this.capacity;
	}
}

class LRUCache {
    private LRULinkedHashMap<Integer, Integer> LRUMap; // 按访问顺序保存

    public LRUCache(int capacity) {
        this.LRUMap = new LRULinkedHashMap(capacity);
    }

    public int get(int key) {
    		Integer value = LRUMap.get(key);
    		if(value==null) return -1;
    		return value;
    }

    public void put(int key, int value) {
    		LRUMap.put(key, value);
    }
}

Java解题—使用LinkedHashMap-2

import java.util.LinkedHashMap;
import java.util.Map.Entry;
class LRUCache {

    private LinkedHashMap<Integer, Integer> map;
    private int capacity;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true){
            @Override
            protected boolean removeEldestEntry(Entry<Integer, Integer> eldest) {
                return this.size()> capacity;
            }
        };
    }

    public int get(int key) {
        Integer value = this.map.get(key);
        if(value==null) return -1;
        return value;
    }

    public void put(int key, int value) {
        this.map.put(key, value);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值