设计并实现一个满足LRU约束的数据结构

电子说

1.3w人已加入

描述

题目

请你设计并实现一个满足 「LRU (最近最少使用) 缓存」 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity)「正整数」 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,

则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 「逐出」 最久未使用的关键字。

cache技术

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

提示:

cache技术

题解(哈希表 + 双向链表)

解题之前先了解一下什么是 「LRU缓存」LRU 的英文全称为 Latest Recently Used,即 「最近最少使用」 。在缓存占满的时候,先删除最旧的数据。

cache技术

Java 代码实现

class LRUCache {

    private int capacity;

    private Map< Integer, ListNode > cache;

    // 保护节点,protectHead.next 为head节点, protectTail.pre为tail节点
    private ListNode protectHead = new ListNode();
    private ListNode protectTail = new ListNode();

    public LRUCache(int capacity) {
        this.capacity = capacity;
        cache = new HashMap<  >(capacity);
        protectHead.next = protectTail;
        protectTail.pre = protectHead;
    }


    // 删除指定节点
    private void remove(ListNode listNode){
        listNode.pre.next = listNode.next;
        listNode.next.pre = listNode.pre;

        listNode.pre = null;
        listNode.next = null;
    }

    // 添加到末尾
    private void addToTail(ListNode listNode){

        protectTail.pre.next = listNode;
        listNode.pre = protectTail.pre;

        listNode.next = protectTail;
        protectTail.pre = listNode;
        
    }

    // 从当前位置移动到末尾
    private void moveToTail(ListNode listNode){
        
        this.remove(listNode);
        this.addToTail(listNode);
        
    }
    
    public int get(int key) {
        if(cache.containsKey(key)){
            ListNode listNode = cache.get(key);
            this.moveToTail(listNode);
            return listNode.value;
        }else{
            return -1;
        }

    }
    
    public void put(int key, int value) {
        if(cache.containsKey(key)){
            // 将 key 移动到最新的位置
            // 1. 在旧的位置删除
            // 2. 追加key到链表末尾
            ListNode listNode = cache.get(key);

            // 这里必须重新赋值,虽然缓冲已经存在了,但是可能值不一样。
            listNode.value = value;
            this.moveToTail(listNode);
            return;

        }

        if(cache.size() == capacity){
            // 1. 找到最旧的数据,也就是链表的head,删除head
            // 2. 在cache map 中删除 head对应的key
            ListNode headNode = protectHead.next;
            this.remove(headNode);
            cache.remove(headNode.key);
        }


        // 1. 添加新的key到cache map
        // 2. 追加新的key到链表末尾

        ListNode listNode = new ListNode();
        listNode.key = key;
        listNode.value = value;

        this.addToTail(listNode);
        cache.put(key, listNode);

    }
}

class ListNode{
    int key;
    int value;
    ListNode pre;
    ListNode next;

}

Go 代码实现

// 定义双向链表
type MyListNode struct {
    key, value int
    pre, next *MyListNode
}

type LRUCache struct { 
    size, capacity int
    cache map[int]*MyListNode
    protectHead, protectTail *MyListNode
}


func Constructor(capacity int) LRUCache {
    lruCache := LRUCache{
        size: 0,
        capacity: capacity,
        cache: map[int]*MyListNode{},
        protectHead: &MyListNode{},
        protectTail: &MyListNode{},
    }

    lruCache.protectHead.next = lruCache.protectTail
    lruCache.protectTail.pre = lruCache.protectHead
    return lruCache
}


func (this *LRUCache) Get(key int) int {
    if listNode, ok := this.cache[key]; ok {
        this.moveToTail(listNode);
        return listNode.value;
    }else{
        return -1
    }
}


func (this *LRUCache) Put(key int, value int)  {
    if listNode, ok := this.cache[key]; ok {
        listNode.value = value;
        this.moveToTail(listNode);
        return;
    }
    if(this.size == this.capacity){
        headNode := this.protectHead.next;
        this.remove(headNode);
        delete(this.cache, headNode.key);
        this.size--
    }

    listNode := &MyListNode{
        key: key,
        value: value,
    }


    this.addToTail(listNode)
    this.cache[key] = listNode
    this.size++
}

// 删除指定节点
func (this *LRUCache) remove(listNode *MyListNode){
    listNode.pre.next = listNode.next;
    listNode.next.pre = listNode.pre;

    listNode.pre = nil;
    listNode.next = nil;
}


// 添加到末尾
func (this *LRUCache) addToTail(listNode *MyListNode){
    this.protectTail.pre.next = listNode
    listNode.pre = this.protectTail.pre

    listNode.next = this.protectTail
    this.protectTail.pre = listNode
}


// 从当前位置移动到末尾
func (this *LRUCache) moveToTail(listNode *MyListNode){
    this.remove(listNode);
    this.addToTail(listNode);
}

复杂度分析

cache技术

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分