设计和实现一个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缓存类的主要内容,如果未能解决你的问题,请参考以下文章