设计和实现一个LRU缓存类

Posted 半梦半醒时

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计和实现一个LRU缓存类相关的知识,希望对你有一定的参考价值。

题目:

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「key-value」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

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

思路:

如果是O(n)的时间复杂度,那么可以把数据存在slice中,每次查询或者插入时,都遍历一遍,但是要在O(1)时间复杂度内实现,必然要使用哈希表,也就是要使用空间来换时间。

在具体实现上,哈希表用于存储值,双向链表用于记录最新使用过的和最久未使用的数据,方便进行增删改查。

关键部分:

在head和tail的地方使用哨兵节点,有边界条件,可以省去判断相邻节点是否存在的情况(举个例子?)

代码实现(哈希表+双向链表):

package mainimport (    "fmt")type LRUCache struct {    capacity   int    size       int    cache      map[int]*DataLinkNode    head, tail *DataLinkNode}type DataLinkNode struct {    key, value int    next, prev *DataLinkNode}//初始化缓存func Constructor(capacity int) *LRUCache {    head := InitialNode(0, 0)    tail := InitialNode(0, 0)    head.next = tail    tail.prev = head    return &LRUCache{        capacity: capacity,        cache:    make(map[int]*DataLinkNode, capacity),        head:     head,        tail:     tail,    }}//初始化节点func InitialNode(key, value int) *DataLinkNode {    return &DataLinkNode{        key:   key,        value: value,    }}func (l *LRUCache) Get(key int) int {    if _, ok := l.cache[key]; !ok {        return -1 //key不存在    }    node := l.cache[key]    l.removeNode(node)    l.moveToHead(node)    return node.value}func (l *LRUCache) Put(key, value int) {    if _, ok := l.cache[key]; !ok { // key不存在        l.addToHead(InitialNode(key, value))        l.size++        if l.size > l.capacity {            l.removeTail()            delete(l.cache, key) //记得删除去掉的节点            l.size--        }    } else {        l.cache[key].value = value        node := l.cache[key]        l.moveToHead(node)    }}//增加节点到headfunc (l *LRUCache) addToHead(node *DataLinkNode) {    node.next = l.head.next    node.prev = l.head    l.head.next.prev = node    l.head.next = node}//移动节点到headfunc (l *LRUCache) moveToHead(node *DataLinkNode) {    l.removeNode(node)    l.addToHead(node)}//删除一个节点func (l *LRUCache) removeNode(node *DataLinkNode) {    node.prev.next = node.next    node.next.prev = node.prev}//删除最后一个节点func (l *LRUCache) removeTail() *DataLinkNode {    node := l.tail.prev    l.removeNode(node) //删除一个节点,直接调用自己的removeNode方法    return node}func main() {    cache1 := Constructor(3)    cache1.Put(1, 1)    cache1.Put(2, 2)    cache1.Get(1)    cache1.Put(3, 3)    cache1.Put(4, 4)    for cache1.head != nil {        fmt.Println(cache1.head.key)        cache1.head = cache1.head.next    }}

这道题感觉还是比较麻烦,指针指来指去很容易出问题,写的时候要很细心。

补充一个数组版本的LRU实现:

type LRUCache struct {    len     int    dataList []Data //存储key,value}type Data struct {    Key int    Val int}func Constructor(capacity int) LRUCache {    return LRUCache{        len:capacity,    }}func (this *LRUCache) Get(key int) int {    isExist := false  //标志是否存在    index := 0    tmp := Data{}    for i, val := range this.dataList{ //存在key,把index和value取出        if val.Key == key {     //同时标记isExist为true            isExist = true            index = i             tmp = val             break        }    }    if isExist {        for ; index < len(this.dataList)-1; index++ {            this.dataList[index] = this.dataList[index+1]        } //需要整体左移,最右边的数据是最新使用过的        this.dataList[index] = tmp        return tmp.Val    }    return -1}func (this *LRUCache) Put(key int, value int)  {    isExist := false //标志位    index := 0  //索引位    for i, val := range this.dataList {        if val.Key == key {  //如果存在key,取出index,设置isExist为true            isExist = true            index = i             break        }    }    if isExist {        for ; index < len(this.dataList)-1; index++ {            this.dataList[index] = this.dataList[index+1]        }//整体左移,最新使用过的数据放在最右边        this.dataList[index] = Data{Key:key, Val:value}    } else {//如果缓存已经满了,整体左移,然后把数据放在最右边一位        if len(this.dataList) == this.len {            for i := 0; i < this.len-1; i++ {                this.dataList[i] = this.dataList[i+1]            }            this.dataList[this.len-1] = Data{Key:key, Val:value}        } else {//如果没有满,直接插入在最后            this.dataList = append(this.dataList,Data{Key:key, Val:value})        }    }}/** * Your LRUCache object will be instantiated and called as such: * obj := Constructor(capacity); * param_1 := obj.Get(key); * obj.Put(key,value); */

以上是关于设计和实现一个LRU缓存类的主要内容,如果未能解决你的问题,请参考以下文章

leetcode热题Hot100——LRU缓存

146. LRU 缓存机制

LeetCode 146. LRU 缓存

队列5:LRU的设计

LeetCode 146. LRU 缓存机制

Leetcode No.146 LRU 缓存机制