【LeetCode】146、LRU 缓存【中等】

一、题目

题目
力扣题解

设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

二、思路

LRU 意为 Latest Recently Used Cache,即 最近最少使用,既然是 使用,即 get 和 put 这两种操作均会让被操作的页优先级提高,从而不被淘汰。

为了实现优先级提高,常用的数据结构(数组、链表、队列、堆、栈、树),我们只能选择链表,因为只需要线性结构所以不选择树,因为需要改变节点顺序所以选择链表。通过链表即可实现将最近时间内最少被使用的页淘汰的特性。

为了实现 O(1)的时间复杂度,而选择的链表却是 O(N),因此需要哈希表的辅助。

链表和哈希表这两者怎么结合呢?通过让哈希表的值是链表节点的地址来实现。

即下图结构:

在这里插入图片描述

以leetocde的输入数据为例,说明具体执行过程:

首先,LRUCache lRUCache = new LRUCache(2);

其次,lRUCache.put(1, 1); 在 HashTable 放入 k = 1,v = *list.Element;List 中向Head端添加一个 *list.Element,值为 entry{k=1,v=1}。此时 List 为 {1=1}

lRUCache.put(2, 2); 在 HashTable 放入 k = 2,v = *list.Element;List 中向Head端添加一个 *list.Element,值为 entry{k=2,v=2}。此时 List 为 {2=2},{1=1}

lRUCache.get(1); 通过 k = 1,在 HashTable 中找到 *list.Element,返回其值为 entry{k=1,v=1}; 并将该 *List.Element 调整到 List 的 Head 位置处。并调整 HashTable 指向新 *list.Element。此时 List 为 {1=1},{2=2}

lRUCache.put(3, 3); 此时 List 为 {3=3},{1=1},将 {2=2}逐出List,并将{2=2}逐出HashTable。

lRUCache.get(2); 通过 HashTable 未查到则返回 -1。List 未改变。

lRUCache.put(4, 4); List 变为 {4=4},{3=3}

lRUCache.get(1); 通过 HashTable 未查到则返回 -1。List 未改变。

lRUCache.get(3); 通过 HashTable 查到,List 调整为 {3=3},{4=4}

lRUCache.get(4); 通过 HashTable 查到,List 调整为 {4=4},{3=3}

三、编码

type entry struct {
    
    k, v int}

type LRUCache struct {
    
    
    cap int
    mp  map[int]*list.Element
    lst *list.List
}


func Constructor(capacity int) LRUCache {
    
    
    return LRUCache{
    
    cap: capacity, mp: map[int]*list.Element{
    
    }, lst: list.New()}
}


func (this *LRUCache) Get(key int) int {
    
    
    e := this.mp[key]
    if e == nil {
    
    
        return -1
    }
    this.lst.MoveToFront(e)
    return e.Value.(entry).v
}


func (this *LRUCache) Put(key int, value int)  {
    
    
    if e := this.mp[key]; e != nil {
    
    
        e.Value = entry{
    
    k: key, v: value}
        this.lst.MoveToFront(e)
        return
    }
    this.mp[key] = this.lst.PushFront(entry{
    
    k: key, v: value})
    if len(this.mp) > this.cap {
    
    
        back := this.lst.Back()
        e := this.lst.Remove(back)
        delete(this.mp, e.(entry).k)
    }
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */

猜你喜欢

转载自blog.csdn.net/jiaoyangwm/article/details/127415890